mirror of
https://github.com/ershisan99/coolify.git
synced 2025-12-29 12:33:59 +00:00
Compare commits
36 Commits
v4.0.0-bet
...
v4.0.0-bet
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5682ab9570 | ||
|
|
a3d73634e7 | ||
|
|
98b6aec203 | ||
|
|
7feb788ed3 | ||
|
|
bea490081b | ||
|
|
7adc3ca003 | ||
|
|
f8cbc63ab0 | ||
|
|
418590fb35 | ||
|
|
56144482f1 | ||
|
|
59f681e6af | ||
|
|
d3296f5180 | ||
|
|
c6fff0aa13 | ||
|
|
41e0c42282 | ||
|
|
ede2274816 | ||
|
|
ead672afb2 | ||
|
|
73bc7b045e | ||
|
|
3281502c25 | ||
|
|
ca35e536db | ||
|
|
bb8e0eb7bf | ||
|
|
bb451ac3b5 | ||
|
|
b0b7842f9c | ||
|
|
aad661cb65 | ||
|
|
ed9b63520d | ||
|
|
5de1246827 | ||
|
|
edc3b014cd | ||
|
|
fec98f45ce | ||
|
|
94810d5066 | ||
|
|
431cc796d8 | ||
|
|
e9d2dbcc92 | ||
|
|
7a618ef89c | ||
|
|
5b56249d12 | ||
|
|
e2ba5abe76 | ||
|
|
70a4b7c863 | ||
|
|
10fde1b1ef | ||
|
|
6131746180 | ||
|
|
bf953bf1b5 |
@@ -34,6 +34,7 @@ Special thanks to our biggest sponsor, [CCCareers](https://cccareers.org/)!
|
||||
|
||||
## Github Sponsors ($40+)
|
||||
<a href="https://bc.direct"><img width="60px" alt="BC Direct" src="https://github.com/coollabsio/coolify/assets/5845193/a4063c41-95ed-4a32-8814-cd1475572e37"/></a>
|
||||
<a href="https://serpapi.com/?utm_source=coolify.io"><img width="60px" alt="SerpAPI" src="https://github.com/serpapi.png"/></a>
|
||||
<a href="https://typebot.io/?utm_source=coolify.io"><img src="https://pbs.twimg.com/profile_images/1509194008366657543/9I-C7uWT_400x400.jpg" width="60px" alt="typebot"/></a>
|
||||
<a href="https://www.quantcdn.io/?utm_source=coolify.io"><img src="https://github.com/quantcdn.png" width="60px" alt="QuantCDN"/></a>
|
||||
<a href="https://www.runpod.io/?utm_source=coolify.io">
|
||||
|
||||
@@ -50,8 +50,9 @@ class StartMongodb
|
||||
],
|
||||
'healthcheck' => [
|
||||
'test' => [
|
||||
'CMD-SHELL',
|
||||
'mongosh --eval "printjson(db.runCommand(\"ping\"))"'
|
||||
"CMD",
|
||||
"echo",
|
||||
"ok"
|
||||
],
|
||||
'interval' => '5s',
|
||||
'timeout' => '5s',
|
||||
|
||||
@@ -6,14 +6,12 @@ use App\Actions\Database\StartDatabaseProxy;
|
||||
use App\Actions\Proxy\CheckProxy;
|
||||
use App\Actions\Proxy\StartProxy;
|
||||
use App\Actions\Shared\ComplexStatusCheck;
|
||||
use App\Models\Application;
|
||||
use App\Models\ApplicationPreview;
|
||||
use App\Models\Server;
|
||||
use App\Models\ServiceDatabase;
|
||||
use App\Notifications\Container\ContainerRestarted;
|
||||
use App\Notifications\Container\ContainerStopped;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Lorisleiva\Actions\Concerns\AsAction;
|
||||
|
||||
class GetContainersStatus
|
||||
@@ -24,9 +22,9 @@ class GetContainersStatus
|
||||
|
||||
public function handle(Server $server)
|
||||
{
|
||||
if (isDev()) {
|
||||
$server = Server::find(0);
|
||||
}
|
||||
// if (isDev()) {
|
||||
// $server = Server::find(0);
|
||||
// }
|
||||
$this->server = $server;
|
||||
if (!$this->server->isFunctional()) {
|
||||
return 'Server is not ready.';
|
||||
@@ -154,7 +152,7 @@ class GetContainersStatus
|
||||
if ($isPublic) {
|
||||
$foundTcpProxy = $containers->filter(function ($value, $key) use ($uuid) {
|
||||
if ($this->server->isSwarm()) {
|
||||
// TODO: fix this with sentinel
|
||||
// TODO: fix this with sentinel
|
||||
return data_get($value, 'Spec.Name') === "coolify-proxy_$uuid";
|
||||
} else {
|
||||
return data_get($value, 'name') === "$uuid-proxy";
|
||||
@@ -316,7 +314,7 @@ class GetContainersStatus
|
||||
$this->server->proxyType();
|
||||
$foundProxyContainer = $containers->filter(function ($value, $key) {
|
||||
if ($this->server->isSwarm()) {
|
||||
// TODO: fix this with sentinel
|
||||
// TODO: fix this with sentinel
|
||||
return data_get($value, 'Spec.Name') === 'coolify-proxy_traefik';
|
||||
} else {
|
||||
return data_get($value, 'name') === 'coolify-proxy';
|
||||
@@ -442,19 +440,21 @@ class GetContainersStatus
|
||||
if ($database_id) {
|
||||
$service_db = ServiceDatabase::where('id', $database_id)->first();
|
||||
if ($service_db) {
|
||||
$uuid = $service_db->service->uuid;
|
||||
$isPublic = data_get($service_db, 'is_public');
|
||||
if ($isPublic) {
|
||||
$foundTcpProxy = $containers->filter(function ($value, $key) use ($uuid) {
|
||||
if ($this->server->isSwarm()) {
|
||||
return data_get($value, 'Spec.Name') === "coolify-proxy_$uuid";
|
||||
} else {
|
||||
return data_get($value, 'Name') === "/$uuid-proxy";
|
||||
$uuid = data_get($service_db, 'service.uuid');
|
||||
if ($uuid) {
|
||||
$isPublic = data_get($service_db, 'is_public');
|
||||
if ($isPublic) {
|
||||
$foundTcpProxy = $containers->filter(function ($value, $key) use ($uuid) {
|
||||
if ($this->server->isSwarm()) {
|
||||
return data_get($value, 'Spec.Name') === "coolify-proxy_$uuid";
|
||||
} else {
|
||||
return data_get($value, 'Name') === "/$uuid-proxy";
|
||||
}
|
||||
})->first();
|
||||
if (!$foundTcpProxy) {
|
||||
StartDatabaseProxy::run($service_db);
|
||||
// $this->server->team?->notify(new ContainerRestarted("TCP Proxy for {$service_db->service->name}", $this->server));
|
||||
}
|
||||
})->first();
|
||||
if (!$foundTcpProxy) {
|
||||
StartDatabaseProxy::run($service_db);
|
||||
// $this->server->team?->notify(new ContainerRestarted("TCP Proxy for {$service_db->service->name}", $this->server));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,10 +27,10 @@ class UpdateCoolify
|
||||
CleanupDocker::run($this->server, false);
|
||||
$this->latestVersion = get_latest_version_of_coolify();
|
||||
$this->currentVersion = config('version');
|
||||
if ($settings->next_channel) {
|
||||
ray('next channel enabled');
|
||||
$this->latestVersion = 'next';
|
||||
}
|
||||
// if ($settings->next_channel) {
|
||||
// ray('next channel enabled');
|
||||
// $this->latestVersion = 'next';
|
||||
// }
|
||||
if ($force) {
|
||||
$this->update();
|
||||
} else {
|
||||
|
||||
@@ -21,8 +21,10 @@ use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
|
||||
|
||||
class Kernel extends ConsoleKernel
|
||||
{
|
||||
private $all_servers;
|
||||
protected function schedule(Schedule $schedule): void
|
||||
{
|
||||
$this->all_servers = Server::all();
|
||||
if (isDev()) {
|
||||
// Instance Jobs
|
||||
$schedule->command('horizon:snapshot')->everyMinute();
|
||||
@@ -56,7 +58,7 @@ class Kernel extends ConsoleKernel
|
||||
}
|
||||
private function pull_helper_image($schedule)
|
||||
{
|
||||
$servers = Server::all()->where('settings.is_usable', true)->where('settings.is_reachable', true)->where('ip', '!=', '1.2.3.4');
|
||||
$servers = $this->all_servers->where('settings.is_usable', true)->where('settings.is_reachable', true)->where('ip', '!=', '1.2.3.4');
|
||||
foreach ($servers as $server) {
|
||||
if (config('coolify.is_sentinel_enabled')) {
|
||||
$schedule->job(new PullSentinelImageJob($server))->everyFiveMinutes()->onOneServer();
|
||||
@@ -67,12 +69,12 @@ class Kernel extends ConsoleKernel
|
||||
private function check_resources($schedule)
|
||||
{
|
||||
if (isCloud()) {
|
||||
$servers = Server::all()->whereNotNull('team.subscription')->where('team.subscription.stripe_trial_already_ended', false)->where('ip', '!=', '1.2.3.4');
|
||||
$servers = $this->all_servers->whereNotNull('team.subscription')->where('team.subscription.stripe_trial_already_ended', false)->where('ip', '!=', '1.2.3.4');
|
||||
$own = Team::find(0)->servers;
|
||||
$servers = $servers->merge($own);
|
||||
$containerServers = $servers->where('settings.is_swarm_worker', false)->where('settings.is_build_server', false);
|
||||
} else {
|
||||
$servers = Server::all()->where('ip', '!=', '1.2.3.4');
|
||||
$servers = $this->all_servers->where('ip', '!=', '1.2.3.4');
|
||||
$containerServers = $servers->where('settings.is_swarm_worker', false)->where('settings.is_build_server', false);
|
||||
}
|
||||
foreach ($containerServers as $server) {
|
||||
|
||||
@@ -713,10 +713,40 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
||||
private function save_environment_variables()
|
||||
{
|
||||
$envs = collect([]);
|
||||
$local_branch = $this->branch;
|
||||
if ($this->pull_request_id !== 0) {
|
||||
$local_branch = "pull/{$this->pull_request_id}/head";
|
||||
}
|
||||
$sort = $this->application->settings->is_env_sorting_enabled;
|
||||
if ($sort) {
|
||||
$sorted_environment_variables = $this->application->environment_variables->sortBy('key');
|
||||
$sorted_environment_variables_preview = $this->application->environment_variables_preview->sortBy('key');
|
||||
} else {
|
||||
$sorted_environment_variables = $this->application->environment_variables->sortBy('id');
|
||||
$sorted_environment_variables_preview = $this->application->environment_variables_preview->sortBy('id');
|
||||
}
|
||||
$ports = $this->application->main_port();
|
||||
if ($this->pull_request_id !== 0) {
|
||||
$this->env_filename = ".env-pr-$this->pull_request_id";
|
||||
foreach ($this->application->environment_variables_preview as $env) {
|
||||
// Add SOURCE_COMMIT if not exists
|
||||
if ($this->application->environment_variables_preview->where('key', 'SOURCE_COMMIT')->isEmpty()) {
|
||||
if (!is_null($this->commit)) {
|
||||
$envs->push("SOURCE_COMMIT={$this->commit}");
|
||||
} else {
|
||||
$envs->push("SOURCE_COMMIT=unknown");
|
||||
}
|
||||
}
|
||||
if ($this->application->environment_variables_preview->where('key', 'COOLIFY_FQDN')->isEmpty()) {
|
||||
$envs->push("COOLIFY_FQDN={$this->preview->fqdn}");
|
||||
}
|
||||
if ($this->application->environment_variables_preview->where('key', 'COOLIFY_URL')->isEmpty()) {
|
||||
$url = str($this->preview->fqdn)->replace('http://', '')->replace('https://', '');
|
||||
$envs->push("COOLIFY_URL={$url}");
|
||||
}
|
||||
if ($this->application->environment_variables_preview->where('key', 'COOLIFY_BRANCH')->isEmpty()) {
|
||||
$envs->push("COOLIFY_BRANCH={$local_branch}");
|
||||
}
|
||||
foreach ($sorted_environment_variables_preview as $env) {
|
||||
$real_value = $env->real_value;
|
||||
if ($env->version === '4.0.0-beta.239') {
|
||||
$real_value = $env->real_value;
|
||||
@@ -737,30 +767,27 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
||||
if ($this->application->environment_variables_preview->where('key', 'HOST')->isEmpty()) {
|
||||
$envs->push("HOST=0.0.0.0");
|
||||
}
|
||||
} else {
|
||||
$this->env_filename = ".env";
|
||||
// Add SOURCE_COMMIT if not exists
|
||||
if ($this->application->environment_variables_preview->where('key', 'SOURCE_COMMIT')->isEmpty()) {
|
||||
if ($this->application->environment_variables->where('key', 'SOURCE_COMMIT')->isEmpty()) {
|
||||
if (!is_null($this->commit)) {
|
||||
$envs->push("SOURCE_COMMIT={$this->commit}");
|
||||
} else {
|
||||
$envs->push("SOURCE_COMMIT=unknown");
|
||||
}
|
||||
}
|
||||
if ($this->application->environment_variables_preview->where('key', 'COOLIFY_FQDN')->isEmpty()) {
|
||||
$envs->push("COOLIFY_FQDN={$this->preview->fqdn}");
|
||||
if ($this->application->environment_variables->where('key', 'COOLIFY_FQDN')->isEmpty()) {
|
||||
$envs->push("COOLIFY_FQDN={$this->application->fqdn}");
|
||||
}
|
||||
if ($this->application->environment_variables_preview->where('key', 'COOLIFY_URL')->isEmpty()) {
|
||||
$url = str($this->preview->fqdn)->replace('http://', '')->replace('https://', '');
|
||||
if ($this->application->environment_variables->where('key', 'COOLIFY_URL')->isEmpty()) {
|
||||
$url = str($this->application->fqdn)->replace('http://', '')->replace('https://', '');
|
||||
$envs->push("COOLIFY_URL={$url}");
|
||||
}
|
||||
if ($this->application->environment_variables_preview->where('key', 'COOLIFY_BRANCH')->isEmpty()) {
|
||||
$envs->push("COOLIFY_BRANCH={$this->application->git_branch}");
|
||||
$envs->push("COOLIFY_BRANCH={$local_branch}");
|
||||
}
|
||||
$envs = $envs->sort(function ($a, $b) {
|
||||
return strpos($a, '$') === false ? -1 : 1;
|
||||
});
|
||||
} else {
|
||||
$this->env_filename = ".env";
|
||||
foreach ($this->application->environment_variables as $env) {
|
||||
foreach ($sorted_environment_variables as $env) {
|
||||
$real_value = $env->real_value;
|
||||
if ($env->version === '4.0.0-beta.239') {
|
||||
$real_value = $env->real_value;
|
||||
@@ -781,27 +808,6 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
||||
if ($this->application->environment_variables->where('key', 'HOST')->isEmpty()) {
|
||||
$envs->push("HOST=0.0.0.0");
|
||||
}
|
||||
// Add SOURCE_COMMIT if not exists
|
||||
if ($this->application->environment_variables->where('key', 'SOURCE_COMMIT')->isEmpty()) {
|
||||
if (!is_null($this->commit)) {
|
||||
$envs->push("SOURCE_COMMIT={$this->commit}");
|
||||
} else {
|
||||
$envs->push("SOURCE_COMMIT=unknown");
|
||||
}
|
||||
}
|
||||
if ($this->application->environment_variables->where('key', 'COOLIFY_FQDN')->isEmpty()) {
|
||||
$envs->push("COOLIFY_FQDN={$this->application->fqdn}");
|
||||
}
|
||||
if ($this->application->environment_variables->where('key', 'COOLIFY_URL')->isEmpty()) {
|
||||
$url = str($this->application->fqdn)->replace('http://', '')->replace('https://', '');
|
||||
$envs->push("COOLIFY_URL={$url}");
|
||||
}
|
||||
if ($this->application->environment_variables_preview->where('key', 'COOLIFY_BRANCH')->isEmpty()) {
|
||||
$envs->push("COOLIFY_BRANCH={$this->application->git_branch}");
|
||||
}
|
||||
$envs = $envs->sort(function ($a, $b) {
|
||||
return strpos($a, '$') === false ? -1 : 1;
|
||||
});
|
||||
}
|
||||
|
||||
if ($envs->isEmpty()) {
|
||||
@@ -957,9 +963,23 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
||||
"save" => "health_check",
|
||||
"append" => false
|
||||
],
|
||||
|
||||
[
|
||||
"docker inspect --format='{{json .State.Health.Log}}' {$this->container_name}",
|
||||
"hidden" => true,
|
||||
"save" => "health_check_logs",
|
||||
"append" => false
|
||||
],
|
||||
);
|
||||
$this->application_deployment_queue->addLogEntry("Attempt {$counter} of {$this->application->health_check_retries} | Healthcheck status: {$this->saved_outputs->get('health_check')}");
|
||||
$health_check_logs = data_get(collect(json_decode($this->saved_outputs->get('health_check_logs')))->last(), 'Output', '(no logs)');
|
||||
if (empty($health_check_logs)) {
|
||||
$health_check_logs = '(no logs)';
|
||||
}
|
||||
$health_check_return_code = data_get(collect(json_decode($this->saved_outputs->get('health_check_logs')))->last(), 'ExitCode', '(no return code)');
|
||||
if ($health_check_logs !== '(no logs)' || $health_check_return_code !== '(no return code)') {
|
||||
$this->application_deployment_queue->addLogEntry("Healthcheck logs: {$health_check_logs} | Return code: {$health_check_return_code}");
|
||||
}
|
||||
|
||||
if (Str::of($this->saved_outputs->get('health_check'))->replace('"', '')->value() === 'healthy') {
|
||||
$this->newVersionIsHealthy = true;
|
||||
$this->application->update(['status' => 'running']);
|
||||
@@ -993,6 +1013,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
||||
$this->generate_image_names();
|
||||
$this->application_deployment_queue->addLogEntry("Starting pull request (#{$this->pull_request_id}) deployment of {$this->customRepository}:{$this->application->git_branch}.");
|
||||
$this->prepare_builder_image();
|
||||
$this->check_git_if_build_needed();
|
||||
$this->clone_repository();
|
||||
$this->set_base_dir();
|
||||
$this->cleanup_git();
|
||||
@@ -1122,6 +1143,10 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
||||
private function check_git_if_build_needed()
|
||||
{
|
||||
$this->generate_git_import_commands();
|
||||
$local_branch = $this->branch;
|
||||
if ($this->pull_request_id !== 0) {
|
||||
$local_branch = "pull/{$this->pull_request_id}/head";
|
||||
}
|
||||
$private_key = data_get($this->application, 'private_key.private_key');
|
||||
if ($private_key) {
|
||||
$private_key = base64_encode($private_key);
|
||||
@@ -1136,7 +1161,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
||||
executeInDocker($this->deployment_uuid, "chmod 600 /root/.ssh/id_rsa")
|
||||
],
|
||||
[
|
||||
executeInDocker($this->deployment_uuid, "GIT_SSH_COMMAND=\"ssh -o ConnectTimeout=30 -p {$this->customPort} -o Port={$this->customPort} -o LogLevel=ERROR -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i /root/.ssh/id_rsa\" git ls-remote {$this->fullRepoUrl} {$this->branch}"),
|
||||
executeInDocker($this->deployment_uuid, "GIT_SSH_COMMAND=\"ssh -o ConnectTimeout=30 -p {$this->customPort} -o Port={$this->customPort} -o LogLevel=ERROR -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i /root/.ssh/id_rsa\" git ls-remote {$this->fullRepoUrl} {$local_branch}"),
|
||||
"hidden" => true,
|
||||
"save" => "git_commit_sha"
|
||||
],
|
||||
@@ -1144,12 +1169,13 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
||||
} else {
|
||||
$this->execute_remote_command(
|
||||
[
|
||||
executeInDocker($this->deployment_uuid, "GIT_SSH_COMMAND=\"ssh -o ConnectTimeout=30 -p {$this->customPort} -o Port={$this->customPort} -o LogLevel=ERROR -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null\" git ls-remote {$this->fullRepoUrl} {$this->branch}"),
|
||||
executeInDocker($this->deployment_uuid, "GIT_SSH_COMMAND=\"ssh -o ConnectTimeout=30 -p {$this->customPort} -o Port={$this->customPort} -o LogLevel=ERROR -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null\" git ls-remote {$this->fullRepoUrl} {$local_branch}"),
|
||||
"hidden" => true,
|
||||
"save" => "git_commit_sha"
|
||||
],
|
||||
);
|
||||
}
|
||||
ray("GIT_SSH_COMMAND=\"ssh -o ConnectTimeout=30 -p {$this->customPort} -o Port={$this->customPort} -o LogLevel=ERROR -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null\" git ls-remote {$this->fullRepoUrl} {$local_branch}");
|
||||
if ($this->saved_outputs->get('git_commit_sha') && !$this->rollback) {
|
||||
$this->commit = $this->saved_outputs->get('git_commit_sha')->before("\t");
|
||||
$this->application_deployment_queue->commit = $this->commit;
|
||||
@@ -1165,6 +1191,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
||||
if ($this->pull_request_id !== 0) {
|
||||
$this->application_deployment_queue->addLogEntry("Checking out tag pull/{$this->pull_request_id}/head.");
|
||||
}
|
||||
ray($importCommands);
|
||||
$this->execute_remote_command(
|
||||
[
|
||||
$importCommands, "hidden" => true
|
||||
@@ -1178,6 +1205,8 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
||||
"save" => "commit_message"
|
||||
]
|
||||
);
|
||||
ray($this->saved_outputs->get('commit_message'));
|
||||
raY($this->commit);
|
||||
if ($this->saved_outputs->get('commit_message')) {
|
||||
$commit_message = str($this->saved_outputs->get('commit_message'))->limit(47);
|
||||
$this->application_deployment_queue->commit_message = $commit_message->value();
|
||||
@@ -1272,6 +1301,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
||||
private function generate_env_variables()
|
||||
{
|
||||
$this->env_args = collect([]);
|
||||
$this->env_args->put('SOURCE_COMMIT', $this->commit);
|
||||
if ($this->pull_request_id === 0) {
|
||||
foreach ($this->application->build_environment_variables as $env) {
|
||||
if (!is_null($env->real_value)) {
|
||||
@@ -1285,7 +1315,6 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
||||
}
|
||||
}
|
||||
}
|
||||
$this->env_args->put('SOURCE_COMMIT', $this->commit);
|
||||
}
|
||||
|
||||
private function generate_compose_file()
|
||||
@@ -1967,10 +1996,7 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
|
||||
if (!$this->only_this_server) {
|
||||
$this->deploy_to_additional_destinations();
|
||||
}
|
||||
if (!isCloud()) {
|
||||
// TODO: turn off until we have a better solution
|
||||
$this->application->environment->project->team?->notify(new DeploymentSuccess($this->application, $this->deployment_uuid, $this->preview));
|
||||
}
|
||||
$this->application->environment->project->team?->notify(new DeploymentSuccess($this->application, $this->deployment_uuid, $this->preview));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,14 +8,13 @@ use App\Models\Server;
|
||||
use App\Models\Application;
|
||||
use App\Models\Service;
|
||||
use App\Models\Team;
|
||||
use App\Notifications\ScheduledTask\TaskFailed;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\Middleware\WithoutOverlapping;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Collection;
|
||||
use Throwable;
|
||||
|
||||
class ScheduledTaskJob implements ShouldQueue
|
||||
{
|
||||
@@ -114,6 +113,7 @@ class ScheduledTaskJob implements ShouldQueue
|
||||
'message' => $this->task_output ?? $e->getMessage(),
|
||||
]);
|
||||
}
|
||||
$this->team?->notify(new TaskFailed($this->task, $e->getMessage()));
|
||||
// send_internal_notification('ScheduledTaskJob failed with: ' . $e->getMessage());
|
||||
throw $e;
|
||||
}
|
||||
|
||||
@@ -41,7 +41,6 @@ class SendMessageToDiscordJob implements ShouldQueue, ShouldBeEncrypted
|
||||
$payload = [
|
||||
'content' => $this->text,
|
||||
];
|
||||
ray($payload);
|
||||
Http::post($this->webhookUrl, $payload);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,6 +52,9 @@ class Index extends Component
|
||||
|
||||
public function mount()
|
||||
{
|
||||
if (auth()->user()?->isMember() && auth()->user()->currentTeam()->show_boarding === true) {
|
||||
return redirect()->route('dashboard');
|
||||
}
|
||||
$this->privateKeyName = generate_random_name();
|
||||
$this->remoteServerName = generate_random_name();
|
||||
if (isDev()) {
|
||||
|
||||
@@ -16,6 +16,7 @@ class Discord extends Component
|
||||
'team.discord_notifications_deployments' => 'nullable|boolean',
|
||||
'team.discord_notifications_status_changes' => 'nullable|boolean',
|
||||
'team.discord_notifications_database_backups' => 'nullable|boolean',
|
||||
'team.discord_notifications_scheduled_tasks' => 'nullable|boolean',
|
||||
];
|
||||
protected $validationAttributes = [
|
||||
'team.discord_webhook_url' => 'Discord Webhook',
|
||||
|
||||
@@ -28,6 +28,7 @@ class Email extends Component
|
||||
'team.smtp_notifications_deployments' => 'nullable|boolean',
|
||||
'team.smtp_notifications_status_changes' => 'nullable|boolean',
|
||||
'team.smtp_notifications_database_backups' => 'nullable|boolean',
|
||||
'team.smtp_notifications_scheduled_tasks' => 'nullable|boolean',
|
||||
'team.use_instance_email_settings' => 'boolean',
|
||||
'team.resend_enabled' => 'nullable|boolean',
|
||||
'team.resend_api_key' => 'nullable',
|
||||
|
||||
@@ -18,10 +18,12 @@ class Telegram extends Component
|
||||
'team.telegram_notifications_deployments' => 'nullable|boolean',
|
||||
'team.telegram_notifications_status_changes' => 'nullable|boolean',
|
||||
'team.telegram_notifications_database_backups' => 'nullable|boolean',
|
||||
'team.telegram_notifications_scheduled_tasks' => 'nullable|boolean',
|
||||
'team.telegram_notifications_test_message_thread_id' => 'nullable|string',
|
||||
'team.telegram_notifications_deployments_message_thread_id' => 'nullable|string',
|
||||
'team.telegram_notifications_status_changes_message_thread_id' => 'nullable|string',
|
||||
'team.telegram_notifications_database_backups_message_thread_id' => 'nullable|string',
|
||||
'team.telegram_notifications_scheduled_tasks_thread_id' => 'nullable|string',
|
||||
];
|
||||
protected $validationAttributes = [
|
||||
'team.telegram_token' => 'Token',
|
||||
|
||||
@@ -31,7 +31,7 @@ class General extends Component
|
||||
public ?string $initialDockerComposeLocation = null;
|
||||
public ?string $initialDockerComposePrLocation = null;
|
||||
|
||||
public $parsedServices = [];
|
||||
public null|Collection $parsedServices;
|
||||
public $parsedServiceDomains = [];
|
||||
|
||||
protected $listeners = [
|
||||
@@ -118,6 +118,10 @@ class General extends Component
|
||||
{
|
||||
try {
|
||||
$this->parsedServices = $this->application->parseCompose();
|
||||
if (is_null($this->parsedServices) || empty($this->parsedServices)) {
|
||||
$this->dispatch('error', "Failed to parse your docker-compose file. Please check the syntax and try again.");
|
||||
return;
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
$this->dispatch('error', $e->getMessage());
|
||||
}
|
||||
@@ -160,6 +164,10 @@ class General extends Component
|
||||
return;
|
||||
}
|
||||
['parsedServices' => $this->parsedServices, 'initialDockerComposeLocation' => $this->initialDockerComposeLocation, 'initialDockerComposePrLocation' => $this->initialDockerComposePrLocation] = $this->application->loadComposeFile($isInit);
|
||||
if (is_null($this->parsedServices)) {
|
||||
$this->dispatch('error', "Failed to parse your docker-compose file. Please check the syntax and try again.");
|
||||
return;
|
||||
}
|
||||
$compose = $this->application->parseCompose();
|
||||
$services = data_get($compose, 'services');
|
||||
if ($services) {
|
||||
|
||||
@@ -5,7 +5,7 @@ namespace App\Livewire\Project\Application;
|
||||
use App\Actions\Application\StopApplication;
|
||||
use App\Actions\Docker\GetContainersStatus;
|
||||
use App\Events\ApplicationStatusChanged;
|
||||
use App\Jobs\ContainerStatusJob;
|
||||
use App\Jobs\ContainerStatusJob;
|
||||
use App\Jobs\ServerStatusJob;
|
||||
use App\Models\Application;
|
||||
use Livewire\Component;
|
||||
@@ -14,6 +14,8 @@ use Visus\Cuid2\Cuid2;
|
||||
class Heading extends Component
|
||||
{
|
||||
public Application $application;
|
||||
public ?string $lastDeploymentInfo = null;
|
||||
public ?string $lastDeploymentLink = null;
|
||||
public array $parameters;
|
||||
|
||||
protected string $deploymentUuid;
|
||||
@@ -28,6 +30,9 @@ class Heading extends Component
|
||||
public function mount()
|
||||
{
|
||||
$this->parameters = get_route_parameters();
|
||||
$lastDeployment = $this->application->get_last_successful_deployment();
|
||||
$this->lastDeploymentInfo = data_get_str($lastDeployment, 'commit')->limit(7) . ' ' . data_get($lastDeployment, 'commit_message');
|
||||
$this->lastDeploymentLink = $this->application->gitCommitLink(data_get($lastDeployment, 'commit'));
|
||||
}
|
||||
|
||||
public function check_status($showNotification = false)
|
||||
|
||||
@@ -35,7 +35,7 @@ class BackupEdit extends Component
|
||||
public function mount()
|
||||
{
|
||||
$this->parameters = get_route_parameters();
|
||||
if (is_null($this->backup->s3_storage_id)) {
|
||||
if (is_null(data_get($this->backup, 's3_storage_id'))) {
|
||||
$this->backup->s3_storage_id = 'default';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ class BackupExecutions extends Component
|
||||
|
||||
public function cleanupFailed()
|
||||
{
|
||||
$this->backup->executions()->where('status', 'failed')->delete();
|
||||
$this->backup?->executions()->where('status', 'failed')->delete();
|
||||
$this->refreshBackupExecutions();
|
||||
}
|
||||
public function deleteBackup($exeuctionId)
|
||||
|
||||
@@ -12,7 +12,6 @@ class Create extends Component
|
||||
public $type;
|
||||
public function mount()
|
||||
{
|
||||
$services = getServiceTemplates();
|
||||
$type = str(request()->query('type'));
|
||||
$destination_uuid = request()->query('destination');
|
||||
$server_id = request()->query('server_id');
|
||||
@@ -25,83 +24,87 @@ class Create extends Component
|
||||
if (!$environment) {
|
||||
return redirect()->route('dashboard');
|
||||
}
|
||||
if (in_array($type, DATABASE_TYPES)) {
|
||||
if ($type->value() === "postgresql") {
|
||||
$database = create_standalone_postgresql($environment->id, $destination_uuid);
|
||||
} else if ($type->value() === 'redis') {
|
||||
$database = create_standalone_redis($environment->id, $destination_uuid);
|
||||
} else if ($type->value() === 'mongodb') {
|
||||
$database = create_standalone_mongodb($environment->id, $destination_uuid);
|
||||
} else if ($type->value() === 'mysql') {
|
||||
$database = create_standalone_mysql($environment->id, $destination_uuid);
|
||||
} else if ($type->value() === 'mariadb') {
|
||||
$database = create_standalone_mariadb($environment->id, $destination_uuid);
|
||||
} else if ($type->value() === 'keydb') {
|
||||
$database = create_standalone_keydb($environment->id, $destination_uuid);
|
||||
} else if ($type->value() === 'dragonfly') {
|
||||
$database = create_standalone_dragonfly($environment->id, $destination_uuid);
|
||||
} else if ($type->value() === 'clickhouse') {
|
||||
$database = create_standalone_clickhouse($environment->id, $destination_uuid);
|
||||
}
|
||||
return redirect()->route('project.database.configuration', [
|
||||
'project_uuid' => $project->uuid,
|
||||
'environment_name' => $environment->name,
|
||||
'database_uuid' => $database->uuid,
|
||||
]);
|
||||
}
|
||||
if ($type->startsWith('one-click-service-') && !is_null((int)$server_id)) {
|
||||
$oneClickServiceName = $type->after('one-click-service-')->value();
|
||||
$oneClickService = data_get($services, "$oneClickServiceName.compose");
|
||||
$oneClickDotEnvs = data_get($services, "$oneClickServiceName.envs", null);
|
||||
if ($oneClickDotEnvs) {
|
||||
$oneClickDotEnvs = str(base64_decode($oneClickDotEnvs))->split('/\r\n|\r|\n/')->filter(function ($value) {
|
||||
return !empty($value);
|
||||
});
|
||||
}
|
||||
if ($oneClickService) {
|
||||
$destination = StandaloneDocker::whereUuid($destination_uuid)->first();
|
||||
$service_payload = [
|
||||
'name' => "$oneClickServiceName-" . str()->random(10),
|
||||
'docker_compose_raw' => base64_decode($oneClickService),
|
||||
'environment_id' => $environment->id,
|
||||
'service_type' => $oneClickServiceName,
|
||||
'server_id' => (int) $server_id,
|
||||
'destination_id' => $destination->id,
|
||||
'destination_type' => $destination->getMorphClass(),
|
||||
];
|
||||
if ($oneClickServiceName === 'cloudflared') {
|
||||
data_set($service_payload, 'connect_to_docker_network', true);
|
||||
if (isset($type) && isset($destination_uuid) && isset($server_id)) {
|
||||
$services = getServiceTemplates();
|
||||
|
||||
if (in_array($type, DATABASE_TYPES)) {
|
||||
if ($type->value() === "postgresql") {
|
||||
$database = create_standalone_postgresql($environment->id, $destination_uuid);
|
||||
} else if ($type->value() === 'redis') {
|
||||
$database = create_standalone_redis($environment->id, $destination_uuid);
|
||||
} else if ($type->value() === 'mongodb') {
|
||||
$database = create_standalone_mongodb($environment->id, $destination_uuid);
|
||||
} else if ($type->value() === 'mysql') {
|
||||
$database = create_standalone_mysql($environment->id, $destination_uuid);
|
||||
} else if ($type->value() === 'mariadb') {
|
||||
$database = create_standalone_mariadb($environment->id, $destination_uuid);
|
||||
} else if ($type->value() === 'keydb') {
|
||||
$database = create_standalone_keydb($environment->id, $destination_uuid);
|
||||
} else if ($type->value() === 'dragonfly') {
|
||||
$database = create_standalone_dragonfly($environment->id, $destination_uuid);
|
||||
} else if ($type->value() === 'clickhouse') {
|
||||
$database = create_standalone_clickhouse($environment->id, $destination_uuid);
|
||||
}
|
||||
$service = Service::create($service_payload);
|
||||
$service->name = "$oneClickServiceName-" . $service->uuid;
|
||||
$service->save();
|
||||
if ($oneClickDotEnvs?->count() > 0) {
|
||||
$oneClickDotEnvs->each(function ($value) use ($service) {
|
||||
$key = str()->before($value, '=');
|
||||
$value = str(str()->after($value, '='));
|
||||
$generatedValue = $value;
|
||||
if ($value->contains('SERVICE_')) {
|
||||
$command = $value->after('SERVICE_')->beforeLast('_');
|
||||
$generatedValue = generateEnvValue($command->value(), $service);
|
||||
}
|
||||
EnvironmentVariable::create([
|
||||
'key' => $key,
|
||||
'value' => $generatedValue,
|
||||
'service_id' => $service->id,
|
||||
'is_build_time' => false,
|
||||
'is_preview' => false,
|
||||
]);
|
||||
});
|
||||
}
|
||||
$service->parse(isNew: true);
|
||||
return redirect()->route('project.service.configuration', [
|
||||
'service_uuid' => $service->uuid,
|
||||
'environment_name' => $environment->name,
|
||||
return redirect()->route('project.database.configuration', [
|
||||
'project_uuid' => $project->uuid,
|
||||
'environment_name' => $environment->name,
|
||||
'database_uuid' => $database->uuid,
|
||||
]);
|
||||
}
|
||||
if ($type->startsWith('one-click-service-') && !is_null((int)$server_id)) {
|
||||
$oneClickServiceName = $type->after('one-click-service-')->value();
|
||||
$oneClickService = data_get($services, "$oneClickServiceName.compose");
|
||||
$oneClickDotEnvs = data_get($services, "$oneClickServiceName.envs", null);
|
||||
if ($oneClickDotEnvs) {
|
||||
$oneClickDotEnvs = str(base64_decode($oneClickDotEnvs))->split('/\r\n|\r|\n/')->filter(function ($value) {
|
||||
return !empty($value);
|
||||
});
|
||||
}
|
||||
if ($oneClickService) {
|
||||
$destination = StandaloneDocker::whereUuid($destination_uuid)->first();
|
||||
$service_payload = [
|
||||
'name' => "$oneClickServiceName-" . str()->random(10),
|
||||
'docker_compose_raw' => base64_decode($oneClickService),
|
||||
'environment_id' => $environment->id,
|
||||
'service_type' => $oneClickServiceName,
|
||||
'server_id' => (int) $server_id,
|
||||
'destination_id' => $destination->id,
|
||||
'destination_type' => $destination->getMorphClass(),
|
||||
];
|
||||
if ($oneClickServiceName === 'cloudflared') {
|
||||
data_set($service_payload, 'connect_to_docker_network', true);
|
||||
}
|
||||
$service = Service::create($service_payload);
|
||||
$service->name = "$oneClickServiceName-" . $service->uuid;
|
||||
$service->save();
|
||||
if ($oneClickDotEnvs?->count() > 0) {
|
||||
$oneClickDotEnvs->each(function ($value) use ($service) {
|
||||
$key = str()->before($value, '=');
|
||||
$value = str(str()->after($value, '='));
|
||||
$generatedValue = $value;
|
||||
if ($value->contains('SERVICE_')) {
|
||||
$command = $value->after('SERVICE_')->beforeLast('_');
|
||||
$generatedValue = generateEnvValue($command->value(), $service);
|
||||
}
|
||||
EnvironmentVariable::create([
|
||||
'key' => $key,
|
||||
'value' => $generatedValue,
|
||||
'service_id' => $service->id,
|
||||
'is_build_time' => false,
|
||||
'is_preview' => false,
|
||||
]);
|
||||
});
|
||||
}
|
||||
$service->parse(isNew: true);
|
||||
return redirect()->route('project.service.configuration', [
|
||||
'service_uuid' => $service->uuid,
|
||||
'environment_name' => $environment->name,
|
||||
'project_uuid' => $project->uuid,
|
||||
]);
|
||||
}
|
||||
}
|
||||
$this->type = $type->value();
|
||||
}
|
||||
$this->type = $type->value();
|
||||
}
|
||||
public function render()
|
||||
{
|
||||
|
||||
@@ -5,11 +5,11 @@ namespace App\Livewire\Project\Shared\EnvironmentVariable;
|
||||
use App\Models\EnvironmentVariable;
|
||||
use Livewire\Component;
|
||||
use Visus\Cuid2\Cuid2;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class All extends Component
|
||||
{
|
||||
public $resource;
|
||||
public string $resourceClass;
|
||||
public bool $showPreview = false;
|
||||
public ?string $modalId = null;
|
||||
public ?string $variables = null;
|
||||
@@ -19,17 +19,44 @@ class All extends Component
|
||||
'refreshEnvs',
|
||||
'saveKey' => 'submit',
|
||||
];
|
||||
protected $rules = [
|
||||
'resource.settings.is_env_sorting_enabled' => 'required|boolean',
|
||||
];
|
||||
|
||||
public function mount()
|
||||
{
|
||||
$resourceClass = get_class($this->resource);
|
||||
$this->resourceClass = get_class($this->resource);
|
||||
$resourceWithPreviews = ['App\Models\Application'];
|
||||
$simpleDockerfile = !is_null(data_get($this->resource, 'dockerfile'));
|
||||
if (Str::of($resourceClass)->contains($resourceWithPreviews) && !$simpleDockerfile) {
|
||||
if (str($this->resourceClass)->contains($resourceWithPreviews) && !$simpleDockerfile) {
|
||||
$this->showPreview = true;
|
||||
}
|
||||
$this->modalId = new Cuid2(7);
|
||||
$this->sortMe();
|
||||
$this->getDevView();
|
||||
}
|
||||
|
||||
public function sortMe()
|
||||
{
|
||||
if ($this->resourceClass === 'App\Models\Application' && data_get($this->resource, 'build_pack') !== 'dockercompose') {
|
||||
if ($this->resource->settings->is_env_sorting_enabled) {
|
||||
$this->resource->environment_variables = $this->resource->environment_variables->sortBy('key');
|
||||
$this->resource->environment_variables_preview = $this->resource->environment_variables_preview->sortBy('key');
|
||||
} else {
|
||||
$this->resource->environment_variables = $this->resource->environment_variables->sortBy('id');
|
||||
$this->resource->environment_variables_preview = $this->resource->environment_variables_preview->sortBy('id');
|
||||
}
|
||||
}
|
||||
$this->getDevView();
|
||||
}
|
||||
public function instantSave()
|
||||
{
|
||||
if ($this->resourceClass === 'App\Models\Application' && data_get($this->resource, 'build_pack') !== 'dockercompose') {
|
||||
$this->resource->settings->save();
|
||||
$this->dispatch('success', 'Environment variable settings updated.');
|
||||
$this->sortMe();
|
||||
}
|
||||
}
|
||||
public function getDevView()
|
||||
{
|
||||
$this->variables = $this->resource->environment_variables->map(function ($item) {
|
||||
@@ -40,7 +67,7 @@ class All extends Component
|
||||
return "$item->key=(multiline, edit in normal view)";
|
||||
}
|
||||
return "$item->key=$item->value";
|
||||
})->sort()->join('
|
||||
})->join('
|
||||
');
|
||||
if ($this->showPreview) {
|
||||
$this->variablesPreview = $this->resource->environment_variables_preview->map(function ($item) {
|
||||
@@ -51,13 +78,18 @@ class All extends Component
|
||||
return "$item->key=(multiline, edit in normal view)";
|
||||
}
|
||||
return "$item->key=$item->value";
|
||||
})->sort()->join('
|
||||
})->join('
|
||||
');
|
||||
}
|
||||
}
|
||||
public function switch()
|
||||
{
|
||||
$this->view = $this->view === 'normal' ? 'dev' : 'normal';
|
||||
if ($this->view === 'normal') {
|
||||
$this->view = 'dev';
|
||||
} else {
|
||||
$this->view = 'normal';
|
||||
}
|
||||
$this->sortMe();
|
||||
}
|
||||
public function saveVariables($isPreview)
|
||||
{
|
||||
@@ -66,6 +98,7 @@ class All extends Component
|
||||
$this->resource->environment_variables_preview()->whereNotIn('key', array_keys($variables))->delete();
|
||||
} else {
|
||||
$variables = parseEnvFormatToArray($this->variables);
|
||||
ray($variables, $this->variables);
|
||||
$this->resource->environment_variables()->whereNotIn('key', array_keys($variables))->delete();
|
||||
}
|
||||
foreach ($variables as $key => $variable) {
|
||||
|
||||
@@ -13,7 +13,7 @@ class Configuration extends Component
|
||||
public bool $is_auto_update_enabled;
|
||||
public bool $is_registration_enabled;
|
||||
public bool $is_dns_validation_enabled;
|
||||
public bool $next_channel;
|
||||
// public bool $next_channel;
|
||||
protected string $dynamic_config_path = '/data/coolify/proxy/dynamic';
|
||||
protected Server $server;
|
||||
|
||||
@@ -37,7 +37,7 @@ class Configuration extends Component
|
||||
$this->do_not_track = $this->settings->do_not_track;
|
||||
$this->is_auto_update_enabled = $this->settings->is_auto_update_enabled;
|
||||
$this->is_registration_enabled = $this->settings->is_registration_enabled;
|
||||
$this->next_channel = $this->settings->next_channel;
|
||||
// $this->next_channel = $this->settings->next_channel;
|
||||
$this->is_dns_validation_enabled = $this->settings->is_dns_validation_enabled;
|
||||
}
|
||||
|
||||
@@ -47,12 +47,12 @@ class Configuration extends Component
|
||||
$this->settings->is_auto_update_enabled = $this->is_auto_update_enabled;
|
||||
$this->settings->is_registration_enabled = $this->is_registration_enabled;
|
||||
$this->settings->is_dns_validation_enabled = $this->is_dns_validation_enabled;
|
||||
if ($this->next_channel) {
|
||||
$this->settings->next_channel = false;
|
||||
$this->next_channel = false;
|
||||
} else {
|
||||
$this->settings->next_channel = $this->next_channel;
|
||||
}
|
||||
// if ($this->next_channel) {
|
||||
// $this->settings->next_channel = false;
|
||||
// $this->next_channel = false;
|
||||
// } else {
|
||||
// $this->settings->next_channel = $this->next_channel;
|
||||
// }
|
||||
$this->settings->save();
|
||||
$this->dispatch('success', 'Settings updated!');
|
||||
}
|
||||
|
||||
117
app/Livewire/Team/AdminView.php
Normal file
117
app/Livewire/Team/AdminView.php
Normal file
@@ -0,0 +1,117 @@
|
||||
<?php
|
||||
|
||||
namespace App\Livewire\Team;
|
||||
|
||||
use App\Models\Team;
|
||||
use App\Models\User;
|
||||
use Livewire\Component;
|
||||
|
||||
class AdminView extends Component
|
||||
{
|
||||
public $users;
|
||||
public ?string $search = "";
|
||||
public function mount()
|
||||
{
|
||||
if (!isInstanceAdmin()) {
|
||||
return redirect()->route('dashboard');
|
||||
}
|
||||
$this->getUsers();
|
||||
}
|
||||
public function submitSearch()
|
||||
{
|
||||
if ($this->search !== "") {
|
||||
$this->users = User::where(function ($query) {
|
||||
$query->where('name', 'like', "%{$this->search}%")
|
||||
->orWhere('email', 'like', "%{$this->search}%");
|
||||
})->get()->filter(function ($user) {
|
||||
return $user->id !== auth()->id();
|
||||
});
|
||||
} else {
|
||||
$this->getUsers();
|
||||
}
|
||||
}
|
||||
public function getUsers()
|
||||
{
|
||||
$this->users = User::where('id', '!=', auth()->id())->get();
|
||||
// $this->users = User::all();
|
||||
}
|
||||
private function finalizeDeletion(User $user, Team $team)
|
||||
{
|
||||
$servers = $team->servers;
|
||||
foreach ($servers as $server) {
|
||||
$resources = $server->definedResources();
|
||||
foreach ($resources as $resource) {
|
||||
ray("Deleting resource: " . $resource->name);
|
||||
$resource->forceDelete();
|
||||
}
|
||||
ray("Deleting server: " . $server->name);
|
||||
$server->forceDelete();
|
||||
}
|
||||
|
||||
$projects = $team->projects;
|
||||
foreach ($projects as $project) {
|
||||
ray("Deleting project: " . $project->name);
|
||||
$project->forceDelete();
|
||||
}
|
||||
$team->members()->detach($user->id);
|
||||
ray('Deleting team: ' . $team->name);
|
||||
$team->delete();
|
||||
}
|
||||
public function delete($id)
|
||||
{
|
||||
$user = User::find($id);
|
||||
$teams = $user->teams;
|
||||
foreach ($teams as $team) {
|
||||
ray($team->name);
|
||||
$user_alone_in_team = $team->members->count() === 1;
|
||||
if ($team->id === 0) {
|
||||
if ($user_alone_in_team) {
|
||||
ray('user is alone in the root team, do nothing');
|
||||
return $this->dispatch('error', 'User is alone in the root team, cannot delete');
|
||||
}
|
||||
}
|
||||
if ($user_alone_in_team) {
|
||||
ray('user is alone in the team');
|
||||
$this->finalizeDeletion($user, $team);
|
||||
continue;
|
||||
}
|
||||
ray('user is not alone in the team');
|
||||
if ($user->isOwner()) {
|
||||
$found_other_owner_or_admin = $team->members->filter(function ($member) {
|
||||
return $member->pivot->role === 'owner' || $member->pivot->role === 'admin';
|
||||
})->where('id', '!=', $user->id)->first();
|
||||
|
||||
if ($found_other_owner_or_admin) {
|
||||
ray('found other owner or admin');
|
||||
$team->members()->detach($user->id);
|
||||
continue;
|
||||
} else {
|
||||
$found_other_member_who_is_not_owner = $team->members->filter(function ($member) {
|
||||
return $member->pivot->role === 'member';
|
||||
})->first();
|
||||
if ($found_other_member_who_is_not_owner) {
|
||||
ray('found other member who is not owner');
|
||||
$found_other_member_who_is_not_owner->pivot->role = 'owner';
|
||||
$found_other_member_who_is_not_owner->pivot->save();
|
||||
$team->members()->detach($user->id);
|
||||
} else {
|
||||
// This should never happen as if the user is the only member in the team, the team should be deleted already.
|
||||
ray('found no other member who is not owner');
|
||||
$this->finalizeDeletion($user, $team);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
ray('user is not owner');
|
||||
$team->members()->detach($user->id);
|
||||
}
|
||||
}
|
||||
ray("Deleting user: " . $user->name);
|
||||
$user->delete();
|
||||
$this->getUsers();
|
||||
}
|
||||
public function render()
|
||||
{
|
||||
return view('livewire.team.admin-view');
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,7 @@
|
||||
namespace App\Livewire;
|
||||
|
||||
use App\Actions\Server\UpdateCoolify;
|
||||
use App\Models\InstanceSettings;
|
||||
|
||||
use Livewire\Component;
|
||||
use DanHarrin\LivewireRateLimiting\WithRateLimiting;
|
||||
|
||||
@@ -11,6 +11,7 @@ class Upgrade extends Component
|
||||
{
|
||||
use WithRateLimiting;
|
||||
public bool $showProgress = false;
|
||||
public bool $updateInProgress = false;
|
||||
public bool $isUpgradeAvailable = false;
|
||||
public string $latestVersion = '';
|
||||
|
||||
@@ -22,23 +23,17 @@ class Upgrade extends Component
|
||||
if (isDev()) {
|
||||
$this->isUpgradeAvailable = true;
|
||||
}
|
||||
$settings = InstanceSettings::get();
|
||||
if ($settings->next_channel) {
|
||||
$this->isUpgradeAvailable = true;
|
||||
$this->latestVersion = 'next';
|
||||
}
|
||||
}
|
||||
|
||||
public function upgrade()
|
||||
{
|
||||
try {
|
||||
if ($this->showProgress) {
|
||||
if ($this->updateInProgress) {
|
||||
return;
|
||||
}
|
||||
$this->rateLimit(1, 30);
|
||||
$this->showProgress = true;
|
||||
$this->rateLimit(1, 60);
|
||||
$this->updateInProgress = true;
|
||||
UpdateCoolify::run(force: true, async: true);
|
||||
$this->dispatch('success', "Updating Coolify to {$this->latestVersion} version...");
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
|
||||
@@ -113,6 +113,18 @@ class Application extends BaseModel
|
||||
}
|
||||
return null;
|
||||
}
|
||||
public function failedTaskLink($task_uuid)
|
||||
{
|
||||
if (data_get($this, 'environment.project.uuid')) {
|
||||
return route('project.application.scheduled-tasks', [
|
||||
'project_uuid' => data_get($this, 'environment.project.uuid'),
|
||||
'environment_name' => data_get($this, 'environment.name'),
|
||||
'application_uuid' => data_get($this, 'uuid'),
|
||||
'task_uuid' => $task_uuid
|
||||
]);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
public function settings()
|
||||
{
|
||||
return $this->hasOne(ApplicationSetting::class);
|
||||
@@ -192,6 +204,9 @@ class Application extends BaseModel
|
||||
public function gitCommitLink($link): string
|
||||
{
|
||||
if (!is_null($this->source?->html_url) && !is_null($this->git_repository) && !is_null($this->git_branch)) {
|
||||
if (str($this->source->html_url)->contains('bitbucket')) {
|
||||
return "{$this->source->html_url}/{$this->git_repository}/commits/{$link}";
|
||||
}
|
||||
return "{$this->source->html_url}/{$this->git_repository}/commit/{$link}";
|
||||
}
|
||||
if (strpos($this->git_repository, 'git@') === 0) {
|
||||
@@ -454,6 +469,10 @@ class Application extends BaseModel
|
||||
}
|
||||
return false;
|
||||
}
|
||||
public function get_last_successful_deployment()
|
||||
{
|
||||
return ApplicationDeploymentQueue::where('application_id', $this->id)->where('status', 'finished')->where('pull_request_id', 0)->orderBy('created_at', 'desc')->first();
|
||||
}
|
||||
public function get_last_days_deployments()
|
||||
{
|
||||
return ApplicationDeploymentQueue::where('application_id', $this->id)->where('created_at', '>=', now()->subDays(7))->orderBy('created_at', 'desc')->get();
|
||||
@@ -872,7 +891,7 @@ class Application extends BaseModel
|
||||
if (!$composeFileContent) {
|
||||
$this->docker_compose_location = $initialDockerComposeLocation;
|
||||
$this->save();
|
||||
throw new \RuntimeException("Docker Compose file not found at: $workdir$composeFile");
|
||||
throw new \RuntimeException("Docker Compose file not found at: $workdir$composeFile<br><br>Check if you used the right extension (.yaml or .yml) in the compose file name.");
|
||||
} else {
|
||||
$this->docker_compose_raw = $composeFileContent;
|
||||
$this->save();
|
||||
@@ -989,7 +1008,8 @@ class Application extends BaseModel
|
||||
getFilesystemVolumesFromServer($this, $isInit);
|
||||
}
|
||||
|
||||
public function parseHealthcheckFromDockerfile($dockerfile, bool $isInit = false) {
|
||||
public function parseHealthcheckFromDockerfile($dockerfile, bool $isInit = false)
|
||||
{
|
||||
if (str($dockerfile)->contains('HEALTHCHECK') && ($this->isHealthcheckDisabled() || $isInit)) {
|
||||
$healthcheckCommand = null;
|
||||
$lines = $dockerfile->toArray();
|
||||
|
||||
@@ -8,6 +8,18 @@ use Illuminate\Database\Eloquent\Model;
|
||||
class Environment extends Model
|
||||
{
|
||||
protected $guarded = [];
|
||||
|
||||
protected static function booted()
|
||||
{
|
||||
static::deleting(function ($environment) {
|
||||
$shared_variables = $environment->environment_variables();
|
||||
foreach ($shared_variables as $shared_variable) {
|
||||
ray('Deleting environment shared variable: ' . $shared_variable->name);
|
||||
$shared_variable->delete();
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
public function isEmpty()
|
||||
{
|
||||
return $this->applications()->count() == 0 &&
|
||||
|
||||
@@ -25,6 +25,11 @@ class Project extends BaseModel
|
||||
static::deleting(function ($project) {
|
||||
$project->environments()->delete();
|
||||
$project->settings()->delete();
|
||||
$shared_variables = $project->environment_variables();
|
||||
foreach ($shared_variables as $shared_variable) {
|
||||
ray('Deleting project shared variable: ' . $shared_variable->name);
|
||||
$shared_variable->delete();
|
||||
}
|
||||
});
|
||||
}
|
||||
public function environment_variables()
|
||||
@@ -55,6 +60,7 @@ class Project extends BaseModel
|
||||
return $this->hasManyThrough(Application::class, Environment::class);
|
||||
}
|
||||
|
||||
|
||||
public function postgresqls()
|
||||
{
|
||||
return $this->hasManyThrough(StandalonePostgresql::class, Environment::class);
|
||||
@@ -91,4 +97,7 @@ class Project extends BaseModel
|
||||
{
|
||||
return $this->applications()->count() + $this->postgresqls()->count() + $this->redis()->count() + $this->mongodbs()->count() + $this->mysqls()->count() + $this->mariadbs()->count() + $this->keydbs()->count() + $this->dragonflies()->count() + $this->services()->count() + $this->clickhouses()->count();
|
||||
}
|
||||
public function databases() {
|
||||
return $this->postgresqls()->get()->merge($this->redis()->get())->merge($this->mongodbs()->get())->merge($this->mysqls()->get())->merge($this->mariadbs()->get())->merge($this->keydbs()->get())->merge($this->dragonflies()->get())->merge($this->clickhouses()->get());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -653,6 +653,18 @@ class Service extends BaseModel
|
||||
}
|
||||
return null;
|
||||
}
|
||||
public function failedTaskLink($task_uuid)
|
||||
{
|
||||
if (data_get($this, 'environment.project.uuid')) {
|
||||
return route('project.service.scheduled-tasks', [
|
||||
'project_uuid' => data_get($this, 'environment.project.uuid'),
|
||||
'environment_name' => data_get($this, 'environment.name'),
|
||||
'application_uuid' => data_get($this, 'uuid'),
|
||||
'task_uuid' => $task_uuid
|
||||
]);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
public function documentation()
|
||||
{
|
||||
$services = getServiceTemplates();
|
||||
|
||||
@@ -26,6 +26,34 @@ class Team extends Model implements SendsDiscord, SendsEmail
|
||||
throw new \Exception('You are not allowed to update this team.');
|
||||
}
|
||||
});
|
||||
|
||||
static::deleting(function ($team) {
|
||||
$keys = $team->privateKeys;
|
||||
foreach ($keys as $key) {
|
||||
ray('Deleting key: ' . $key->name);
|
||||
$key->delete();
|
||||
}
|
||||
$sources = $team->sources();
|
||||
foreach ($sources as $source) {
|
||||
ray('Deleting source: ' . $source->name);
|
||||
$source->delete();
|
||||
}
|
||||
$tags = Tag::whereTeamId($team->id)->get();
|
||||
foreach ($tags as $tag) {
|
||||
ray('Deleting tag: ' . $tag->name);
|
||||
$tag->delete();
|
||||
}
|
||||
$shared_variables = $team->environment_variables();
|
||||
foreach ($shared_variables as $shared_variable) {
|
||||
ray('Deleting team shared variable: ' . $shared_variable->name);
|
||||
$shared_variable->delete();
|
||||
}
|
||||
$s3s = $team->s3s;
|
||||
foreach ($s3s as $s3) {
|
||||
ray('Deleting s3: ' . $s3->name);
|
||||
$s3->delete();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public function routeNotificationForDiscord()
|
||||
|
||||
@@ -43,7 +43,12 @@ class DeploymentSuccess extends Notification implements ShouldQueue
|
||||
|
||||
public function via(object $notifiable): array
|
||||
{
|
||||
return setNotificationChannels($notifiable, 'deployments');
|
||||
$channels = setNotificationChannels($notifiable, 'deployments');
|
||||
if (isCloud()) {
|
||||
// TODO: Make batch notifications work with email
|
||||
$channels = array_diff($channels, ['App\Notifications\Channels\EmailChannel']);
|
||||
}
|
||||
return $channels;
|
||||
}
|
||||
public function toMail(): MailMessage
|
||||
{
|
||||
|
||||
@@ -14,22 +14,27 @@ class TelegramChannel
|
||||
$buttons = data_get($data, 'buttons', []);
|
||||
$telegramToken = data_get($telegramData, 'token');
|
||||
$chatId = data_get($telegramData, 'chat_id');
|
||||
$topicId = null;
|
||||
$topicId = null;
|
||||
$topicsInstance = get_class($notification);
|
||||
|
||||
switch ($topicsInstance) {
|
||||
case 'App\Notifications\StatusChange':
|
||||
$topicId = data_get($notifiable, 'telegram_notifications_status_changes_message_thread_id');
|
||||
break;
|
||||
case 'App\Notifications\Test':
|
||||
$topicId = data_get($notifiable, 'telegram_notifications_test_message_thread_id');
|
||||
break;
|
||||
case 'App\Notifications\Deployment':
|
||||
case 'App\Notifications\Application\StatusChanged':
|
||||
$topicId = data_get($notifiable, 'telegram_notifications_status_changes_message_thread_id');
|
||||
break;
|
||||
case 'App\Notifications\Application\DeploymentSuccess':
|
||||
case 'App\Notifications\Application\DeploymentFailed':
|
||||
$topicId = data_get($notifiable, 'telegram_notifications_deployments_message_thread_id');
|
||||
break;
|
||||
case 'App\Notifications\DatabaseBackup':
|
||||
case 'App\Notifications\Database\BackupSuccess':
|
||||
case 'App\Notifications\Database\BackupFailed':
|
||||
$topicId = data_get($notifiable, 'telegram_notifications_database_backups_message_thread_id');
|
||||
break;
|
||||
case 'App\Notifications\ScheduledTask\TaskFailed':
|
||||
$topicId = data_get($notifiable, 'telegram_notifications_scheduled_tasks_thread_id');
|
||||
break;
|
||||
}
|
||||
if (!$telegramToken || !$chatId || !$message) {
|
||||
return;
|
||||
|
||||
@@ -31,7 +31,7 @@ class ContainerRestarted extends Notification implements ShouldQueue
|
||||
$mail->view('emails.container-restarted', [
|
||||
'containerName' => $this->name,
|
||||
'serverName' => $this->server->name,
|
||||
'url' => $this->url ,
|
||||
'url' => $this->url,
|
||||
]);
|
||||
return $mail;
|
||||
}
|
||||
|
||||
64
app/Notifications/ScheduledTask/TaskFailed.php
Normal file
64
app/Notifications/ScheduledTask/TaskFailed.php
Normal file
@@ -0,0 +1,64 @@
|
||||
<?php
|
||||
|
||||
namespace App\Notifications\ScheduledTask;
|
||||
|
||||
use App\Models\ScheduledTask;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Notifications\Messages\MailMessage;
|
||||
use Illuminate\Notifications\Notification;
|
||||
|
||||
class TaskFailed extends Notification implements ShouldQueue
|
||||
{
|
||||
use Queueable;
|
||||
|
||||
public $backoff = 10;
|
||||
public $tries = 2;
|
||||
|
||||
public ?string $url = null;
|
||||
|
||||
public function __construct(public ScheduledTask $task, public string $output)
|
||||
{
|
||||
if ($task->application) {
|
||||
$this->url = $task->application->failedTaskLink($task->uuid);
|
||||
} else if ($task->service) {
|
||||
$this->url = $task->service->failedTaskLink($task->uuid);
|
||||
}
|
||||
}
|
||||
|
||||
public function via(object $notifiable): array
|
||||
{
|
||||
|
||||
return setNotificationChannels($notifiable, 'scheduled_tasks');
|
||||
}
|
||||
|
||||
public function toMail(): MailMessage
|
||||
{
|
||||
$mail = new MailMessage();
|
||||
$mail->subject("Coolify: [ACTION REQUIRED] Scheduled task ({$this->task->name}) failed.");
|
||||
$mail->view('emails.scheduled-task-failed', [
|
||||
'task' => $this->task,
|
||||
'url' => $this->url,
|
||||
'output' => $this->output,
|
||||
]);
|
||||
return $mail;
|
||||
}
|
||||
|
||||
public function toDiscord(): string
|
||||
{
|
||||
return "Coolify: Scheduled task ({$this->task->name}, [link]({$this->url})) failed with output: {$this->output}";
|
||||
}
|
||||
public function toTelegram(): array
|
||||
{
|
||||
$message = "Coolify: Scheduled task ({$this->task->name}) failed with output: {$this->output}";
|
||||
if ($this->url) {
|
||||
$buttons[] = [
|
||||
"text" => "Open task in Coolify",
|
||||
"url" => (string) $this->url
|
||||
];
|
||||
}
|
||||
return [
|
||||
"message" => $message,
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -95,6 +95,9 @@ function currentTeam()
|
||||
|
||||
function showBoarding(): bool
|
||||
{
|
||||
if (auth()->user()?->isMember()) {
|
||||
return false;
|
||||
}
|
||||
return currentTeam()->show_boarding ?? false;
|
||||
}
|
||||
function refreshSession(?Team $team = null): void
|
||||
@@ -1232,13 +1235,13 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
|
||||
try {
|
||||
$yaml = Yaml::parse($resource->docker_compose_pr_raw);
|
||||
} catch (\Exception $e) {
|
||||
throw new \Exception($e->getMessage());
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
$yaml = Yaml::parse($resource->docker_compose_raw);
|
||||
} catch (\Exception $e) {
|
||||
throw new \Exception($e->getMessage());
|
||||
return;
|
||||
}
|
||||
}
|
||||
$server = $resource->destination->server;
|
||||
|
||||
@@ -7,7 +7,7 @@ return [
|
||||
|
||||
// The release version of your application
|
||||
// Example with dynamic git hash: trim(exec('git --git-dir ' . base_path('.git') . ' log --pretty="%h" -n1 HEAD'))
|
||||
'release' => '4.0.0-beta.280',
|
||||
'release' => '4.0.0-beta.285',
|
||||
// When left empty or `null` the Laravel environment will be used
|
||||
'environment' => config('app.env'),
|
||||
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
<?php
|
||||
|
||||
return '4.0.0-beta.280';
|
||||
return '4.0.0-beta.285';
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('application_settings', function (Blueprint $table) {
|
||||
$table->boolean('is_env_sorting_enabled')->default(true);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('application_settings', function (Blueprint $table) {
|
||||
$table->dropColumn('is_env_sorting_enabled');
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('teams', function (Blueprint $table) {
|
||||
$table->boolean('telegram_notifications_scheduled_tasks')->default(true);
|
||||
$table->boolean('smtp_notifications_scheduled_tasks')->default(false)->after('smtp_notifications_status_changes');
|
||||
$table->boolean('discord_notifications_scheduled_tasks')->default(true)->after('discord_notifications_status_changes');
|
||||
$table->text('telegram_notifications_scheduled_tasks_thread_id')->nullable();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('teams', function (Blueprint $table) {
|
||||
$table->dropColumn('telegram_notifications_scheduled_tasks');
|
||||
$table->dropColumn('smtp_notifications_scheduled_tasks');
|
||||
$table->dropColumn('discord_notifications_scheduled_tasks');
|
||||
$table->dropColumn('telegram_notifications_scheduled_tasks_thread_id');
|
||||
});
|
||||
}
|
||||
};
|
||||
42
database/seeders/TestTeamSeeder.php
Normal file
42
database/seeders/TestTeamSeeder.php
Normal file
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Seeders;
|
||||
|
||||
use App\Models\Team;
|
||||
use App\Models\User;
|
||||
use Illuminate\Database\Seeder;
|
||||
|
||||
class TestTeamSeeder extends Seeder
|
||||
{
|
||||
public function run(): void
|
||||
{
|
||||
// User has 2 teams, 1 personal, 1 other where it is the owner and no other members are in the team
|
||||
$user = User::factory()->create([
|
||||
'name' => '1 personal, 1 other team, owner, no other members',
|
||||
'email' => '1@example.com',
|
||||
]);
|
||||
$team = Team::create([
|
||||
'name' => "1@example.com",
|
||||
'personal_team' => false,
|
||||
'show_boarding' => true
|
||||
]);
|
||||
$user->teams()->attach($team, ['role' => 'owner']);
|
||||
|
||||
// User has 2 teams, 1 personal, 1 other where it is the owner and 1 other member is in the team
|
||||
$user = User::factory()->create([
|
||||
'name' => 'owner: 1 personal, 1 other team, owner, 1 other member',
|
||||
'email' => '2@example.com',
|
||||
]);
|
||||
$team = Team::create([
|
||||
'name' => "2@example.com",
|
||||
'personal_team' => false,
|
||||
'show_boarding' => true
|
||||
]);
|
||||
$user->teams()->attach($team, ['role' => 'owner']);
|
||||
$user = User::factory()->create([
|
||||
'name' => 'member: 1 personal, 1 other team, owner, 1 other member',
|
||||
'email' => '3@example.com',
|
||||
]);
|
||||
$team->members()->attach($user, ['role' => 'member']);
|
||||
}
|
||||
}
|
||||
@@ -41,7 +41,7 @@ option {
|
||||
}
|
||||
|
||||
.button {
|
||||
@apply flex items-center justify-center gap-2 px-2 py-1 text-sm text-black normal-case border rounded cursor-pointer bg-neutral-200/50 border-neutral-300 hover:bg-neutral-300 dark:bg-coolgray-200 dark:text-white dark:hover:text-white dark:hover:bg-coolgray-500 dark:border-black hover:text-black disabled:cursor-not-allowed min-w-fit focus:outline-1 dark:disabled:text-neutral-600 disabled:border-none disabled:hover:bg-transparent disabled:bg-transparent disabled:text-neutral-300;
|
||||
@apply flex items-center justify-center gap-2 px-2 py-1 text-sm text-black normal-case border rounded cursor-pointer bg-neutral-200/50 border-neutral-300 hover:bg-neutral-300 dark:bg-coolgray-200 dark:text-white dark:hover:text-white dark:hover:bg-coolgray-500 dark:border-coolgray-300 hover:text-black disabled:cursor-not-allowed min-w-fit focus:outline-1 dark:disabled:text-neutral-600 disabled:border-none disabled:hover:bg-transparent disabled:bg-transparent disabled:text-neutral-300;
|
||||
}
|
||||
|
||||
button[isError]:not(:disabled) {
|
||||
|
||||
@@ -161,10 +161,11 @@
|
||||
class="{{ request()->is('storages*') ? 'menu-item-active menu-item' : 'menu-item' }}"
|
||||
href="{{ route('storage.index') }}">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="icon" viewBox="0 0 24 24">
|
||||
<g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2">
|
||||
<path d="M4 6a8 3 0 1 0 16 0A8 3 0 1 0 4 6"/>
|
||||
<path d="M4 6v6a8 3 0 0 0 16 0V6"/>
|
||||
<path d="M4 12v6a8 3 0 0 0 16 0v-6"/>
|
||||
<g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"
|
||||
stroke-width="2">
|
||||
<path d="M4 6a8 3 0 1 0 16 0A8 3 0 1 0 4 6" />
|
||||
<path d="M4 6v6a8 3 0 0 0 16 0V6" />
|
||||
<path d="M4 12v6a8 3 0 0 0 16 0v-6" />
|
||||
</g>
|
||||
</svg>
|
||||
S3 Storages
|
||||
@@ -175,9 +176,11 @@
|
||||
class="{{ request()->is('shared-variables*') ? 'menu-item-active menu-item' : 'menu-item' }}"
|
||||
href="{{ route('shared-variables.index') }}">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="icon" viewBox="0 0 24 24">
|
||||
<g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2">
|
||||
<path d="M5 4C2.5 9 2.5 14 5 20M19 4c2.5 5 2.5 10 0 16M9 9h1c1 0 1 1 2.016 3.527C13 15 13 16 14 16h1"/>
|
||||
<path d="M8 16c1.5 0 3-2 4-3.5S14.5 9 16 9"/>
|
||||
<g fill="none" stroke="currentColor" stroke-linecap="round"
|
||||
stroke-linejoin="round" stroke-width="2">
|
||||
<path
|
||||
d="M5 4C2.5 9 2.5 14 5 20M19 4c2.5 5 2.5 10 0 16M9 9h1c1 0 1 1 2.016 3.527C13 15 13 16 14 16h1" />
|
||||
<path d="M8 16c1.5 0 3-2 4-3.5S14.5 9 16 9" />
|
||||
</g>
|
||||
</svg>
|
||||
Shared Variables
|
||||
@@ -318,9 +321,7 @@
|
||||
<div class="flex-1"></div>
|
||||
@if (isInstanceAdmin() && !isCloud())
|
||||
<li>
|
||||
@persist('upgrade')
|
||||
<livewire:upgrade />
|
||||
@endpersist
|
||||
<livewire:upgrade />
|
||||
</li>
|
||||
@endif
|
||||
<li>
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
@props([
|
||||
'lastDeploymentInfo' => null,
|
||||
'lastDeploymentLink' => null,
|
||||
'resource' => null,
|
||||
])
|
||||
<nav class="flex pt-2 pb-10">
|
||||
<ol class="flex items-center flex-wrap gap-y-1">
|
||||
<ol class="flex flex-wrap items-center gap-y-1">
|
||||
<li class="inline-flex items-center">
|
||||
<div class="flex items-center">
|
||||
<a wire:navigate class="text-xs truncate lg:text-sm"
|
||||
@@ -15,7 +20,6 @@
|
||||
</li>
|
||||
<li>
|
||||
<div class="flex items-center">
|
||||
|
||||
<a class="text-xs truncate lg:text-sm"
|
||||
href="{{ route('project.resource.index', ['environment_name' => $this->parameters['environment_name'], 'project_uuid' => $this->parameters['project_uuid']]) }}">{{ $this->parameters['environment_name'] }}</a>
|
||||
<svg aria-hidden="true" class="w-4 h-4 mx-1 font-bold dark:text-warning" fill="currentColor"
|
||||
@@ -40,7 +44,7 @@
|
||||
@if ($resource->getMorphClass() == 'App\Models\Service')
|
||||
<x-status.services :service="$resource" />
|
||||
@else
|
||||
<x-status.index :resource="$resource" />
|
||||
<x-status.index :resource="$resource" :lastDeploymentInfo="$lastDeploymentInfo" :lastDeploymentLink="$lastDeploymentLink" />
|
||||
@endif
|
||||
</ol>
|
||||
</nav>
|
||||
|
||||
@@ -1,9 +1,14 @@
|
||||
@props([
|
||||
'lastDeploymentInfo' => null,
|
||||
'lastDeploymentLink' => null,
|
||||
'resource' => null,
|
||||
])
|
||||
@if (str($resource->status)->startsWith('running'))
|
||||
<x-status.running :status="$resource->status" />
|
||||
<x-status.running :status="$resource->status" :lastDeploymentInfo="$lastDeploymentInfo" :lastDeploymentLink="$lastDeploymentLink" />
|
||||
@elseif(str($resource->status)->startsWith('restarting') ||
|
||||
str($resource->status)->startsWith('starting') ||
|
||||
str($resource->status)->startsWith('degraded'))
|
||||
<x-status.restarting :status="$resource->status" />
|
||||
<x-status.restarting :status="$resource->status" :lastDeploymentInfo="$lastDeploymentInfo" :lastDeploymentLink="$lastDeploymentLink" />
|
||||
@else
|
||||
<x-status.stopped :status="$resource->status" />
|
||||
@endif
|
||||
|
||||
@@ -1,12 +1,20 @@
|
||||
@props([
|
||||
'status' => 'Restarting',
|
||||
'lastDeploymentInfo' => null,
|
||||
'lastDeploymentLink' => null,
|
||||
])
|
||||
<div class="flex items-center">
|
||||
<x-loading wire:loading.delay.longer />
|
||||
<span wire:loading.remove.delay.longer class="flex items-center">
|
||||
<div class="badge badge-warning "></div>
|
||||
<div class="pl-2 pr-1 text-xs font-bold tracking-wider dark:text-warning">
|
||||
{{ str($status)->before(':')->headline() }}
|
||||
<div class="pl-2 pr-1 text-xs font-bold tracking-wider dark:text-warning" @if($lastDeploymentInfo) title="{{$lastDeploymentInfo}}" @endif>
|
||||
@if ($lastDeploymentLink)
|
||||
<a href="{{ $lastDeploymentLink }}" class="underline cursor-pointer">
|
||||
{{ str($status)->before(':')->headline() }}
|
||||
</a>
|
||||
@else
|
||||
{{ str($status)->before(':')->headline() }}
|
||||
@endif
|
||||
</div>
|
||||
@if (!str($status)->startsWith('Proxy') && !str($status)->contains('('))
|
||||
<div class="text-xs dark:text-warning">({{ str($status)->after(':') }})</div>
|
||||
|
||||
@@ -1,12 +1,20 @@
|
||||
@props([
|
||||
'status' => 'Running',
|
||||
'lastDeploymentInfo' => null,
|
||||
'lastDeploymentLink' => null,
|
||||
])
|
||||
<div class="flex items-center">
|
||||
<x-loading wire:loading.delay.longer />
|
||||
<span wire:loading.remove.delay.longer class="flex items-center">
|
||||
<div class="badge badge-success "></div>
|
||||
<div class="pl-2 pr-1 text-xs font-bold tracking-wider text-success">
|
||||
<div class="pl-2 pr-1 text-xs font-bold tracking-wider text-success" @if($lastDeploymentInfo) title="{{$lastDeploymentInfo}}" @endif>
|
||||
@if ($lastDeploymentLink)
|
||||
<a href="{{ $lastDeploymentLink }}" class="underline cursor-pointer">
|
||||
{{ str($status)->before(':')->headline() }}
|
||||
</a>
|
||||
@else
|
||||
{{ str($status)->before(':')->headline() }}
|
||||
@endif
|
||||
</div>
|
||||
@if (!str($status)->startsWith('Proxy') && !str($status)->contains('('))
|
||||
@if (str($status)->contains('unhealthy'))
|
||||
@@ -17,8 +25,6 @@
|
||||
</svg>
|
||||
</x-slot:icon>
|
||||
</x-helper>
|
||||
{{-- @else
|
||||
<div class="text-xs dark:text-success">({{ str($status)->after(':') }})</div> --}}
|
||||
@endif
|
||||
@endif
|
||||
</span>
|
||||
|
||||
@@ -15,6 +15,12 @@
|
||||
href="{{ route('team.member.index') }}">
|
||||
<button>Members</button>
|
||||
</a>
|
||||
@if (isInstanceAdmin())
|
||||
<a class="{{ request()->routeIs('team.admin-view') ? 'dark:text-white' : '' }}"
|
||||
href="{{ route('team.admin-view') }}">
|
||||
<button>Admin View</button>
|
||||
</a>
|
||||
@endif
|
||||
<div class="flex-1"></div>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
9
resources/views/emails/scheduled-task-failed.blade.php
Normal file
9
resources/views/emails/scheduled-task-failed.blade.php
Normal file
@@ -0,0 +1,9 @@
|
||||
<x-emails.layout>
|
||||
Scheduled task ({{ $task->name }}) was FAILED with the following error:
|
||||
|
||||
<pre>
|
||||
{{ $output }}
|
||||
</pre>
|
||||
|
||||
Click [here]({{ $url }}) to view the task.
|
||||
</x-emails.layout>
|
||||
@@ -98,47 +98,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
function revive() {
|
||||
if (checkHealthInterval) return true;
|
||||
console.log('Checking server\'s health...')
|
||||
checkHealthInterval = setInterval(() => {
|
||||
fetch('/api/health')
|
||||
.then(response => {
|
||||
if (response.ok) {
|
||||
window.toast('Coolify is back online. Reloading...', {
|
||||
type: 'success',
|
||||
})
|
||||
if (checkHealthInterval) clearInterval(checkHealthInterval);
|
||||
setTimeout(() => {
|
||||
window.location.reload();
|
||||
}, 5000)
|
||||
} else {
|
||||
console.log('Waiting for server to come back from dead...');
|
||||
}
|
||||
})
|
||||
}, 2000);
|
||||
}
|
||||
|
||||
function upgrade() {
|
||||
if (checkIfIamDeadInterval) return true;
|
||||
console.log('Update initiated.')
|
||||
checkIfIamDeadInterval = setInterval(() => {
|
||||
fetch('/api/health')
|
||||
.then(response => {
|
||||
if (response.ok) {
|
||||
console.log('It\'s alive. Waiting for server to be dead...');
|
||||
} else {
|
||||
window.toast('Update done, restarting Coolify!', {
|
||||
type: 'success',
|
||||
})
|
||||
console.log('It\'s dead. Reviving... Standby... Bzz... Bzz...')
|
||||
if (checkIfIamDeadInterval) clearInterval(checkIfIamDeadInterval);
|
||||
revive();
|
||||
}
|
||||
})
|
||||
}, 2000);
|
||||
}
|
||||
|
||||
function copyToClipboard(text) {
|
||||
navigator?.clipboard?.writeText(text) && window.Livewire.dispatch('success', 'Copied to clipboard.');
|
||||
}
|
||||
|
||||
@@ -32,6 +32,8 @@
|
||||
label="Application Deployments" />
|
||||
<x-forms.checkbox instantSave="saveModel" id="team.discord_notifications_database_backups"
|
||||
label="Backup Status" />
|
||||
<x-forms.checkbox instantSave="saveModel" id="team.discord_notifications_scheduled_tasks"
|
||||
label="Scheduled Tasks Status" />
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
@@ -111,6 +111,8 @@
|
||||
label="Application Deployments" />
|
||||
<x-forms.checkbox instantSave="saveModel" id="team.smtp_notifications_database_backups"
|
||||
label="Backup Status" />
|
||||
<x-forms.checkbox instantSave="saveModel" id="team.smtp_notifications_scheduled_tasks"
|
||||
label="Scheduled Tasks Status" />
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
Save
|
||||
</x-forms.button>
|
||||
@if ($team->telegram_enabled)
|
||||
<x-forms.button class="dark:text-white normal-case btn btn-xs no-animation btn-primary"
|
||||
<x-forms.button class="normal-case dark:text-white btn btn-xs no-animation btn-primary"
|
||||
wire:click="sendTestNotification">
|
||||
Send Test Notifications
|
||||
</x-forms.button>
|
||||
@@ -18,44 +18,57 @@
|
||||
</div>
|
||||
<div class="flex gap-2">
|
||||
<x-forms.input type="password"
|
||||
helper="Get it from the <a class='inline-block dark:text-white underline' href='https://t.me/botfather' target='_blank'>BotFather Bot</a> on Telegram."
|
||||
helper="Get it from the <a class='inline-block underline dark:text-white' href='https://t.me/botfather' target='_blank'>BotFather Bot</a> on Telegram."
|
||||
required id="team.telegram_token" label="Token" />
|
||||
<x-forms.input helper="Recommended to add your bot to a group chat and add its Chat ID here." required
|
||||
id="team.telegram_chat_id" label="Chat ID" />
|
||||
</div>
|
||||
@if (data_get($team, 'telegram_enabled'))
|
||||
<h2 class="mt-4">Subscribe to events</h2>
|
||||
<div class="w-96">
|
||||
<div class="flex flex-col gap-4 w-96">
|
||||
@if (isDev())
|
||||
<div class="w-64">
|
||||
<div class="flex flex-col">
|
||||
<h4>Test Notification</h4>
|
||||
<x-forms.checkbox instantSave="saveModel" id="team.telegram_notifications_test"
|
||||
label="Test" />
|
||||
label="Enabled" />
|
||||
<x-forms.input
|
||||
helper="If you are using Group chat with Topics, you can specify the topics ID. If empty, General topic will be used."
|
||||
id="team.telegram_notifications_test_message_thread_id" label="Custom Topic ID" />
|
||||
</div>
|
||||
@endif
|
||||
<div class="w-64">
|
||||
<div class="flex flex-col">
|
||||
<h4>Container Status Changes</h4>
|
||||
<x-forms.checkbox instantSave="saveModel" id="team.telegram_notifications_status_changes"
|
||||
label="Container Status Changes" />
|
||||
label="Enabled" />
|
||||
<x-forms.input
|
||||
helper="If you are using Group chat with Topics, you can specify the topics ID. If empty, General topic will be used."
|
||||
id="team.telegram_notifications_status_changes_message_thread_id" label="Custom Topic ID" />
|
||||
</div>
|
||||
<div class="w-64">
|
||||
<div class="flex flex-col">
|
||||
<h4>Application Deployments</h4>
|
||||
<x-forms.checkbox instantSave="saveModel" id="team.telegram_notifications_deployments"
|
||||
label="Application Deployments" />
|
||||
label="Enabled" />
|
||||
<x-forms.input
|
||||
helper="If you are using Group chat with Topics, you can specify the topics ID. If empty, General topic will be used."
|
||||
id="team.telegram_notifications_deployments_message_thread_id" label="Custom Topic ID" />
|
||||
</div>
|
||||
<div class="w-64">
|
||||
<div class="flex flex-col">
|
||||
<h4>Database Backup Status</h4>
|
||||
<x-forms.checkbox instantSave="saveModel" id="team.telegram_notifications_database_backups"
|
||||
label="Backup Status" />
|
||||
label="Enabled" />
|
||||
<x-forms.input
|
||||
helper="If you are using Group chat with Topics, you can specify the topics ID. If empty, General topic will be used."
|
||||
id="team.telegram_notifications_database_backups_message_thread_id" label="Custom Topic ID" />
|
||||
</div>
|
||||
<div class="flex flex-col">
|
||||
<h4>Scheduled Tasks Status</h4>
|
||||
<x-forms.checkbox instantSave="saveModel" id="team.telegram_notifications_scheduled_tasks"
|
||||
label="Enabled" />
|
||||
<x-forms.input
|
||||
helper="If you are using Group chat with Topics, you can specify the topics ID. If empty, General topic will be used."
|
||||
id="team.telegram_notifications_scheduled_tasks_thread_id" label="Custom Topic ID" />
|
||||
</div>
|
||||
|
||||
</div>
|
||||
@endif
|
||||
</form>
|
||||
|
||||
@@ -38,7 +38,11 @@
|
||||
</div>
|
||||
@endif
|
||||
@if ($application->build_pack === 'dockercompose')
|
||||
@if (count($parsedServices) > 0 && !$application->settings->is_raw_compose_deployment_enabled)
|
||||
|
||||
@if (
|
||||
!is_null($parsedServices) &&
|
||||
count($parsedServices) > 0 &&
|
||||
!$application->settings->is_raw_compose_deployment_enabled)
|
||||
<h3 class="pt-6">Domains</h3>
|
||||
@foreach (data_get($parsedServices, 'services') as $serviceName => $service)
|
||||
@if (!isDatabaseImage(data_get($service, 'image')))
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<nav wire:poll.5000ms="check_status">
|
||||
<x-resources.breadcrumbs :resource="$application" :parameters="$parameters" />
|
||||
<x-resources.breadcrumbs :resource="$application" :parameters="$parameters" :lastDeploymentInfo="$lastDeploymentInfo" :lastDeploymentLink="$lastDeploymentLink" />
|
||||
<div class="navbar-main">
|
||||
<nav class="flex items-center flex-shrink-0 gap-6 scrollbar min-h-10 whitespace-nowrap">
|
||||
<a href="{{ route('project.application.configuration', $parameters) }}">
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
</div>
|
||||
@if (!$branch_found)
|
||||
<div class="px-2 pt-4">
|
||||
<div class="flex gap-1">
|
||||
<div class="flex flex-col pb-4">
|
||||
<div>Preselect branch (eg: main):</div>
|
||||
<div class='text-helper'>https://github.com/coollabsio/coolify-examples/tree/main</div>
|
||||
</div>
|
||||
|
||||
@@ -11,12 +11,18 @@
|
||||
wire:click='switch'>{{ $view === 'normal' ? 'Developer view' : 'Normal view' }}</x-forms.button>
|
||||
</div>
|
||||
<div>Environment variables (secrets) for this resource.</div>
|
||||
@if ($this->resourceClass === 'App\Models\Application' && data_get($this->resource, 'build_pack') !== 'dockercompose')
|
||||
<div class="w-64 pt-2">
|
||||
<x-forms.checkbox id="resource.settings.is_env_sorting_enabled" label="Sort alphabetically"
|
||||
helper="Turn this off if one environment is dependent on an other. It will be sorted by creation order." instantSave></x-forms.checkbox>
|
||||
</div>
|
||||
@endif
|
||||
@if ($resource->type() === 'service' || $resource?->build_pack === 'dockercompose')
|
||||
<div class="pt-4 dark:text-warning text-coollabs">Hardcoded variables are not shown here.</div>
|
||||
@endif
|
||||
</div>
|
||||
@if ($view === 'normal')
|
||||
@forelse ($resource->environment_variables->sort()->sortBy('key') as $env)
|
||||
@forelse ($resource->environment_variables as $env)
|
||||
<livewire:project.shared.environment-variable.show wire:key="environment-{{ $env->id }}"
|
||||
:env="$env" :type="$resource->type()" />
|
||||
@empty
|
||||
@@ -27,7 +33,7 @@
|
||||
<h3>Preview Deployments</h3>
|
||||
<div>Environment (secrets) variables for Preview Deployments.</div>
|
||||
</div>
|
||||
@foreach ($resource->environment_variables_preview->sort()->sortBy('key') as $env)
|
||||
@foreach ($resource->environment_variables_preview as $env)
|
||||
<livewire:project.shared.environment-variable.show wire:key="environment-{{ $env->id }}"
|
||||
:env="$env" :type="$resource->type()" />
|
||||
@endforeach
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<div>General configuration for your Coolify instance.</div>
|
||||
|
||||
<div class="flex flex-col gap-2 pt-4">
|
||||
<div class="flex items-end gap-2 flex-wrap">
|
||||
<div class="flex flex-wrap items-end gap-2">
|
||||
<x-forms.input id="settings.fqdn" label="Instance's Domain" placeholder="https://coolify.io" />
|
||||
<x-forms.input id="settings.custom_dns_servers" label="DNS Servers"
|
||||
helper="DNS servers for validation FQDNs againts. A comma separated list of DNS servers."
|
||||
@@ -35,13 +35,13 @@
|
||||
@endif
|
||||
<x-forms.checkbox instantSave id="is_registration_enabled" label="Registration Allowed" />
|
||||
<x-forms.checkbox instantSave id="do_not_track" label="Do Not Track" />
|
||||
@if ($next_channel)
|
||||
{{-- @if ($next_channel)
|
||||
<x-forms.checkbox instantSave helper="Not recommended. Only if you like to live on the edge."
|
||||
id="next_channel" label="Enable pre-release (early) updates" />
|
||||
@else
|
||||
<x-forms.checkbox disabled instantSave
|
||||
helper="Currently disabled. Not recommended. Only if you like to live on the edge." id="next_channel"
|
||||
label="Enable pre-release (early) updates" />
|
||||
@endif
|
||||
@endif --}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
29
resources/views/livewire/team/admin-view.blade.php
Normal file
29
resources/views/livewire/team/admin-view.blade.php
Normal file
@@ -0,0 +1,29 @@
|
||||
<div>
|
||||
<x-team.navbar />
|
||||
<form wire:submit="submitSearch" class="flex flex-col gap-2 lg:flex-row">
|
||||
<x-forms.input wire:model="search" placeholder="Search for a user" />
|
||||
<x-forms.button type="submit">Search</x-forms.button>
|
||||
</form>
|
||||
<h3 class="pt-4">Users</h3>
|
||||
<div class="flex flex-col gap-2 ">
|
||||
@forelse ($users as $user)
|
||||
<div class="flex items-center justify-center gap-2 bg-white box-without-bg dark:bg-coolgray-100">
|
||||
<div>{{ $user->name }}</div>
|
||||
<div>{{ $user->email }}</div>
|
||||
<div class="flex-1"></div>
|
||||
<div class="flex items-center justify-center gap-2 mx-4 text-xs font-bold ">
|
||||
<x-modal-confirmation isErrorButton action="delete({{ $user->id }})" buttonTitle="Delete">
|
||||
This will delete all resources (application, databases, services, configurations, servers,
|
||||
private keys, tags, etc.) from Coolify and <span
|
||||
class="font-bold text-red-500 dark:text-warning">from the server (if it's reachable)</span>.
|
||||
<br> <br>
|
||||
It is not reversible. <br><br>
|
||||
<div class="font-bold text-red-500 dark:text-white">Think twice!</div>
|
||||
</x-modal-confirmation>
|
||||
</div>
|
||||
</div>
|
||||
@empty
|
||||
<div>No users found other than the root.</div>
|
||||
@endforelse
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,5 +1,5 @@
|
||||
<div>
|
||||
<form wire:submit='viaLink' class="flex items-center gap-2">
|
||||
<form wire:submit='viaLink' class="flex flex-col gap-2 lg:items-center lg:flex-row">
|
||||
<x-forms.input id="email" type="email" name="email" placeholder="Email" />
|
||||
<x-forms.select id="role" name="role">
|
||||
<option value="owner">Owner</option>
|
||||
|
||||
@@ -1,29 +1,139 @@
|
||||
<div @if ($isUpgradeAvailable) title="New version available" @else title="No upgrade available" @endif
|
||||
x-init="$wire.checkUpdate" x-data>
|
||||
x-init="$wire.checkUpdate" x-data="upgradeModal">
|
||||
@if ($isUpgradeAvailable)
|
||||
<button wire:key="upgrade" wire:click='upgrade' class="menu-item" x-on:click="upgrade">
|
||||
@if ($showProgress)
|
||||
<svg xmlns="http://www.w3.org/2000/svg"
|
||||
class="w-6 h-6 text-pink-500 transition-colors hover:text-pink-300 lds-heart" 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 d="M19.5 13.572l-7.5 7.428l-7.5 -7.428m0 0a5 5 0 1 1 7.5 -6.566a5 5 0 1 1 7.5 6.572" />
|
||||
</svg>
|
||||
In progress
|
||||
@else
|
||||
<svg xmlns="http://www.w3.org/2000/svg"
|
||||
class="w-6 h-6 text-pink-500 transition-colors hover:text-pink-300" 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
|
||||
d="M9 12h-3.586a1 1 0 0 1 -.707 -1.707l6.586 -6.586a1 1 0 0 1 1.414 0l6.586 6.586a1 1 0 0 1 -.707 1.707h-3.586v3h-6v-3z" />
|
||||
<path d="M9 21h6" />
|
||||
<path d="M9 18h6" />
|
||||
</svg>
|
||||
Upgrade
|
||||
@endif
|
||||
</button>
|
||||
<div :class="{ 'z-40': modalOpen }" class="relative w-auto h-auto">
|
||||
<button class="menu-item" @click="modalOpen=true">
|
||||
@if ($showProgress)
|
||||
<svg xmlns="http://www.w3.org/2000/svg"
|
||||
class="w-6 h-6 text-pink-500 transition-colors hover:text-pink-300 lds-heart" 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 d="M19.5 13.572l-7.5 7.428l-7.5 -7.428m0 0a5 5 0 1 1 7.5 -6.566a5 5 0 1 1 7.5 6.572" />
|
||||
</svg>
|
||||
In progress
|
||||
@else
|
||||
<svg xmlns="http://www.w3.org/2000/svg"
|
||||
class="w-6 h-6 text-pink-500 transition-colors hover:text-pink-300" 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
|
||||
d="M9 12h-3.586a1 1 0 0 1 -.707 -1.707l6.586 -6.586a1 1 0 0 1 1.414 0l6.586 6.586a1 1 0 0 1 -.707 1.707h-3.586v3h-6v-3z" />
|
||||
<path d="M9 21h6" />
|
||||
<path d="M9 18h6" />
|
||||
</svg>
|
||||
Upgrade
|
||||
@endif
|
||||
</button>
|
||||
|
||||
<template x-teleport="body">
|
||||
<div x-show="modalOpen"
|
||||
class="fixed top-0 lg:pt-10 left-0 z-[99] flex items-start justify-center w-screen h-screen"
|
||||
x-cloak>
|
||||
<div x-show="modalOpen" x-transition:enter="ease-out duration-100"
|
||||
x-transition:enter-start="opacity-0" x-transition:enter-end="opacity-100"
|
||||
x-transition:leave="ease-in duration-100" x-transition:leave-start="opacity-100"
|
||||
x-transition:leave-end="opacity-0"
|
||||
class="absolute inset-0 w-full h-full bg-black bg-opacity-20 backdrop-blur-sm"></div>
|
||||
<div x-show="modalOpen" x-trap.inert.noscroll="modalOpen" x-transition:enter="ease-out duration-100"
|
||||
x-transition:enter-start="opacity-0 -translate-y-2 sm:scale-95"
|
||||
x-transition:enter-end="opacity-100 translate-y-0 sm:scale-100"
|
||||
x-transition:leave="ease-in duration-100"
|
||||
x-transition:leave-start="opacity-100 translate-y-0 sm:scale-100"
|
||||
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-fit bg-neutral-100 border-neutral-400 dark:bg-base px-7 dark:border-coolgray-300">
|
||||
<div class="flex items-center justify-between pb-3">
|
||||
<h3 class="text-lg font-semibold">Upgrade confirmation</h3>
|
||||
@if (!$showProgress)
|
||||
<button @click="modalOpen=false"
|
||||
class="absolute top-0 right-0 flex items-center justify-center w-8 h-8 mt-5 mr-5 text-gray-600 rounded-full hover:text-gray-800 hover:bg-gray-50">
|
||||
<svg class="w-5 h-5" xmlns="http://www.w3.org/2000/svg" fill="none"
|
||||
viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" />
|
||||
</svg>
|
||||
</button>
|
||||
@endif
|
||||
</div>
|
||||
<div class="relative w-auto pb-8">
|
||||
<p>Are you sure you would like to upgrade your instance to {{ $latestVersion }}?</p>
|
||||
<br />
|
||||
<p>You can review the changelogs <a class="font-bold underline"
|
||||
href="https://github.com/coollabsio/coolify/releases" target="_blank">here</a>.</p>
|
||||
@if ($showProgress)
|
||||
<div class="flex flex-col pt-4">
|
||||
<h4>Progress <x-loading /></h4>
|
||||
<div x-html="currentStatus"></div>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
<div class="flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2">
|
||||
@if (!$showProgress)
|
||||
<x-forms.button @click="modalOpen=false"
|
||||
class="w-24 dark:bg-coolgray-200 dark:hover:bg-coolgray-300">Cancel
|
||||
</x-forms.button>
|
||||
<x-forms.button @click="confirmed" class="w-24" isHighlighted type="button">Continue
|
||||
</x-forms.button>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
<script>
|
||||
document.addEventListener('alpine:init', () => {
|
||||
Alpine.data('upgradeModal', () => ({
|
||||
modalOpen: false,
|
||||
showProgress: @js($showProgress),
|
||||
currentStatus: '',
|
||||
confirmed() {
|
||||
this.$wire.$call('upgrade')
|
||||
this.upgrade();
|
||||
this.$wire.showProgress = true;
|
||||
},
|
||||
revive() {
|
||||
if (checkHealthInterval) return true;
|
||||
console.log('Checking server\'s health...')
|
||||
checkHealthInterval = setInterval(() => {
|
||||
fetch('/api/health')
|
||||
.then(response => {
|
||||
if (response.ok) {
|
||||
this.currentStatus =
|
||||
'Coolify is back online. Reloading this page (you can manually reload if its not done automatically)...';
|
||||
if (checkHealthInterval) clearInterval(
|
||||
checkHealthInterval);
|
||||
setTimeout(() => {
|
||||
window.location.reload();
|
||||
}, 5000)
|
||||
} else {
|
||||
this.currentStatus =
|
||||
"Waiting for Coolify to come back from dead..."
|
||||
}
|
||||
})
|
||||
}, 2000);
|
||||
},
|
||||
upgrade() {
|
||||
if (checkIfIamDeadInterval || this.$wire.showProgress) return true;
|
||||
this.currentStatus = 'Pulling new images and updating Coolify.';
|
||||
checkIfIamDeadInterval = setInterval(() => {
|
||||
fetch('/api/health')
|
||||
.then(response => {
|
||||
if (response.ok) {
|
||||
this.currentStatus = "Waiting for the update process..."
|
||||
} else {
|
||||
this.currentStatus =
|
||||
"Update done, restarting Coolify & waiting until it is revived!"
|
||||
if (checkIfIamDeadInterval) clearInterval(
|
||||
checkIfIamDeadInterval);
|
||||
this.revive();
|
||||
}
|
||||
})
|
||||
}, 2000);
|
||||
}
|
||||
|
||||
}))
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -82,7 +82,7 @@ use App\Livewire\Subscription\Show as SubscriptionShow;
|
||||
|
||||
use App\Livewire\Tags\Index as TagsIndex;
|
||||
use App\Livewire\Tags\Show as TagsShow;
|
||||
|
||||
use App\Livewire\Team\AdminView as TeamAdminView;
|
||||
use App\Livewire\Waitlist\Index as WaitlistIndex;
|
||||
use App\Models\ScheduledDatabaseBackupExecution;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
@@ -160,6 +160,7 @@ Route::middleware(['auth', 'verified'])->group(function () {
|
||||
Route::prefix('team')->group(function () {
|
||||
Route::get('/', TeamIndex::class)->name('team.index');
|
||||
Route::get('/members', TeamMemberIndex::class)->name('team.member.index');
|
||||
Route::get('/admin', TeamAdminView::class)->name('team.admin-view');
|
||||
});
|
||||
|
||||
Route::get('/command-center', CommandCenterIndex::class)->name('command-center');
|
||||
|
||||
@@ -135,7 +135,6 @@ if [ "$SSH_PERMIT_ROOT_LOGIN" != "true" ]; then
|
||||
echo "WARNING: PermitRootLogin is not enabled in /etc/ssh/sshd_config."
|
||||
echo -e "It is set to $SSH_PERMIT_ROOT_LOGIN_CONFIG. Should be prohibit-password, yes or without-password.\n"
|
||||
echo -e "Please make sure it is set, otherwise Coolify cannot connect to the host system. \n"
|
||||
echo "(Currently we only support root user to login via SSH, this will be changed in the future.)"
|
||||
echo "###############################################################################"
|
||||
fi
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ services:
|
||||
redis:
|
||||
condition: service_started
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:80"]
|
||||
test: ["CMD", "curl", "-f", "http://127.0.0.1:80"]
|
||||
interval: 5s
|
||||
timeout: 20s
|
||||
retries: 10
|
||||
|
||||
@@ -16,7 +16,7 @@ services:
|
||||
volumes:
|
||||
- stacks-data:/appsmith-stacks
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:80"]
|
||||
test: ["CMD", "curl", "-f", "http://127.0.0.1:80"]
|
||||
interval: 5s
|
||||
timeout: 20s
|
||||
retries: 10
|
||||
|
||||
@@ -15,7 +15,7 @@ services:
|
||||
volumes:
|
||||
- babybuddy-config:/config
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:8000"]
|
||||
test: ["CMD", "curl", "-f", "http://127.0.0.1:8000"]
|
||||
interval: 2s
|
||||
timeout: 10s
|
||||
retries: 15
|
||||
|
||||
@@ -13,7 +13,7 @@ services:
|
||||
volumes:
|
||||
- budge-config:/config
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:80"]
|
||||
test: ["CMD", "curl", "-f", "http://127.0.0.1:80"]
|
||||
interval: 2s
|
||||
timeout: 10s
|
||||
retries: 15
|
||||
|
||||
@@ -17,7 +17,7 @@ services:
|
||||
depends_on:
|
||||
- mariadb
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost"]
|
||||
test: ["CMD", "curl", "-f", "http://127.0.0.1"]
|
||||
interval: 2s
|
||||
timeout: 10s
|
||||
retries: 10
|
||||
|
||||
@@ -17,7 +17,7 @@ services:
|
||||
depends_on:
|
||||
- mysql
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost"]
|
||||
test: ["CMD", "curl", "-f", "http://127.0.0.1"]
|
||||
interval: 2s
|
||||
timeout: 10s
|
||||
retries: 10
|
||||
@@ -31,7 +31,7 @@ services:
|
||||
- MYSQL_USER=$SERVICE_USER_CLASSICPRESS
|
||||
- MYSQL_PASSWORD=$SERVICE_PASSWORD_CLASSICPRESS
|
||||
healthcheck:
|
||||
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
|
||||
test: ["CMD", "mysqladmin", "ping", "-h", "127.0.0.1"]
|
||||
interval: 5s
|
||||
timeout: 20s
|
||||
retries: 10
|
||||
|
||||
@@ -11,7 +11,7 @@ services:
|
||||
environment:
|
||||
- SERVICE_FQDN_CLASSICPRESS
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost"]
|
||||
test: ["CMD", "curl", "-f", "http://127.0.0.1"]
|
||||
interval: 2s
|
||||
timeout: 10s
|
||||
retries: 10
|
||||
|
||||
@@ -18,7 +18,7 @@ services:
|
||||
volumes:
|
||||
- code-server-config:/config
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:8443"]
|
||||
test: ["CMD", "curl", "-f", "http://127.0.0.1:8443"]
|
||||
interval: 2s
|
||||
timeout: 10s
|
||||
retries: 15
|
||||
|
||||
@@ -11,7 +11,7 @@ services:
|
||||
volumes:
|
||||
- dashboard-data:/app/data
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:8080"]
|
||||
test: ["CMD", "curl", "-f", "http://127.0.0.1:8080"]
|
||||
interval: 2s
|
||||
timeout: 10s
|
||||
retries: 15
|
||||
|
||||
@@ -26,7 +26,7 @@ services:
|
||||
- REDIS_PORT=6379
|
||||
- WEBSOCKETS_ENABLED=true
|
||||
healthcheck:
|
||||
test: ["CMD", "wget", "-q", "--spider", "http://localhost:8055/admin/login"]
|
||||
test: ["CMD", "wget", "-q", "--spider", "http://127.0.0.1:8055/admin/login"]
|
||||
interval: 5s
|
||||
timeout: 20s
|
||||
retries: 10
|
||||
|
||||
@@ -22,7 +22,7 @@ services:
|
||||
- WEBSOCKETS_ENABLED=true
|
||||
healthcheck:
|
||||
test:
|
||||
["CMD", "wget", "-q", "--spider", "http://localhost:8055/admin/login"]
|
||||
["CMD", "wget", "-q", "--spider", "http://127.0.0.1:8055/admin/login"]
|
||||
interval: 5s
|
||||
timeout: 20s
|
||||
retries: 10
|
||||
|
||||
@@ -14,7 +14,7 @@ services:
|
||||
volumes:
|
||||
- dokuwiki-config:/config
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:80"]
|
||||
test: ["CMD", "curl", "-f", "http://127.0.0.1:80"]
|
||||
interval: 2s
|
||||
timeout: 10s
|
||||
retries: 15
|
||||
|
||||
@@ -16,7 +16,7 @@ services:
|
||||
- duplicati-config:/config
|
||||
- duplicati-backups:/backups
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:8200"]
|
||||
test: ["CMD", "curl", "-f", "http://127.0.0.1:8200"]
|
||||
interval: 2s
|
||||
timeout: 10s
|
||||
retries: 15
|
||||
|
||||
@@ -17,7 +17,7 @@ services:
|
||||
- emby-tvshows:/tvshows
|
||||
- emby-movies:/movies
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:8096"]
|
||||
test: ["CMD", "curl", "-f", "http://127.0.0.1:8096"]
|
||||
interval: 2s
|
||||
timeout: 10s
|
||||
retries: 15
|
||||
|
||||
@@ -14,7 +14,7 @@ services:
|
||||
volumes:
|
||||
- embystat-config:/config
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:6555"]
|
||||
test: ["CMD", "curl", "-f", "http://127.0.0.1:6555"]
|
||||
interval: 2s
|
||||
timeout: 10s
|
||||
retries: 15
|
||||
|
||||
@@ -20,7 +20,7 @@ services:
|
||||
read_only: true
|
||||
content: "{}"
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:80"]
|
||||
test: ["CMD", "curl", "-f", "http://127.0.0.1:80"]
|
||||
interval: 2s
|
||||
timeout: 10s
|
||||
retries: 15
|
||||
|
||||
@@ -21,7 +21,7 @@ services:
|
||||
volumes:
|
||||
- firefly-upload:/var/www/html/storage/upload
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:8080"]
|
||||
test: ["CMD", "curl", "-f", "http://127.0.0.1:8080"]
|
||||
interval: 5s
|
||||
timeout: 20s
|
||||
retries: 10
|
||||
@@ -42,7 +42,7 @@ services:
|
||||
"mysqladmin",
|
||||
"ping",
|
||||
"-h",
|
||||
"localhost",
|
||||
"127.0.0.1",
|
||||
"-uroot",
|
||||
"-p${SERVICE_PASSWORD_MYSQLROOT}",
|
||||
]
|
||||
|
||||
@@ -42,7 +42,7 @@ services:
|
||||
postgresql:
|
||||
condition: service_healthy
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:3000"]
|
||||
test: ["CMD", "curl", "-f", "http://127.0.0.1:3000"]
|
||||
interval: 2s
|
||||
timeout: 10s
|
||||
retries: 15
|
||||
|
||||
@@ -41,7 +41,7 @@ services:
|
||||
- MYSQL_DATABASE=${MYSQL_DATABASE}
|
||||
- MYSQL_ROOT_PASSWORD=${SERVICE_PASSWORD_MYSQLROOT}
|
||||
healthcheck:
|
||||
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
|
||||
test: ["CMD", "mysqladmin", "ping", "-h", "127.0.0.1"]
|
||||
interval: 5s
|
||||
timeout: 20s
|
||||
retries: 10
|
||||
|
||||
@@ -25,7 +25,7 @@ services:
|
||||
mariadb:
|
||||
condition: service_healthy
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:3000"]
|
||||
test: ["CMD", "curl", "-f", "http://127.0.0.1:3000"]
|
||||
interval: 2s
|
||||
timeout: 10s
|
||||
retries: 15
|
||||
|
||||
@@ -25,7 +25,7 @@ services:
|
||||
mysql:
|
||||
condition: service_healthy
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:3000"]
|
||||
test: ["CMD", "curl", "-f", "http://127.0.0.1:3000"]
|
||||
interval: 2s
|
||||
timeout: 10s
|
||||
retries: 15
|
||||
@@ -40,7 +40,7 @@ services:
|
||||
- MYSQL_DATABASE=${MYSQL_DATABASE}
|
||||
- MYSQL_ROOT_PASSWORD=${SERVICE_PASSWORD_MYSQLROOT}
|
||||
healthcheck:
|
||||
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
|
||||
test: ["CMD", "mysqladmin", "ping", "-h", "127.0.0.1"]
|
||||
interval: 5s
|
||||
timeout: 20s
|
||||
retries: 10
|
||||
|
||||
@@ -25,7 +25,7 @@ services:
|
||||
postgresql:
|
||||
condition: service_healthy
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:3000"]
|
||||
test: ["CMD", "curl", "-f", "http://127.0.0.1:3000"]
|
||||
interval: 2s
|
||||
timeout: 10s
|
||||
retries: 15
|
||||
|
||||
@@ -17,7 +17,7 @@ services:
|
||||
- gitea-timezone:/etc/timezone:ro
|
||||
- gitea-localtime:/etc/localtime:ro
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:3000"]
|
||||
test: ["CMD", "curl", "-f", "http://127.0.0.1:3000"]
|
||||
interval: 2s
|
||||
timeout: 10s
|
||||
retries: 15
|
||||
|
||||
@@ -20,7 +20,7 @@ services:
|
||||
volumes:
|
||||
- grafana-data:/var/lib/grafana
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:3000/api/health"]
|
||||
test: ["CMD", "curl", "-f", "http://127.0.0.1:3000/api/health"]
|
||||
interval: 5s
|
||||
timeout: 20s
|
||||
retries: 10
|
||||
|
||||
@@ -15,7 +15,7 @@ services:
|
||||
volumes:
|
||||
- grafana-data:/var/lib/grafana
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:3000/api/health"]
|
||||
test: ["CMD", "curl", "-f", "http://127.0.0.1:3000/api/health"]
|
||||
interval: 5s
|
||||
timeout: 20s
|
||||
retries: 10
|
||||
|
||||
@@ -14,7 +14,7 @@ services:
|
||||
volumes:
|
||||
- grocy-config:/config
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:80"]
|
||||
test: ["CMD", "curl", "-f", "http://127.0.0.1:80"]
|
||||
interval: 2s
|
||||
timeout: 10s
|
||||
retries: 15
|
||||
|
||||
@@ -13,7 +13,7 @@ services:
|
||||
volumes:
|
||||
- heimdall-config:/config
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:80"]
|
||||
test: ["CMD", "curl", "-f", "http://127.0.0.1:80"]
|
||||
interval: 2s
|
||||
timeout: 10s
|
||||
retries: 15
|
||||
|
||||
@@ -23,7 +23,7 @@ services:
|
||||
- DB_USERNAME=${SERVICE_USER_MYSQL}
|
||||
- DB_PASSWORD=${SERVICE_PASSWORD_MYSQL}
|
||||
healthcheck:
|
||||
test: ['CMD', 'curl', '-f', 'http://localhost:9000']
|
||||
test: ['CMD', 'curl', '-f', 'http://127.0.0.1:9000']
|
||||
interval: 5s
|
||||
timeout: 20s
|
||||
retries: 10
|
||||
@@ -94,7 +94,7 @@ services:
|
||||
"mysqladmin",
|
||||
"ping",
|
||||
"-h",
|
||||
"localhost",
|
||||
"127.0.0.1",
|
||||
"-uroot",
|
||||
"-p${SERVICE_PASSWORD_MYSQLROOT}",
|
||||
]
|
||||
|
||||
@@ -18,7 +18,7 @@ services:
|
||||
- jellyfin-tvshows:/data/tvshows
|
||||
- jellyfin-movies:/data/movies
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:8096"]
|
||||
test: ["CMD", "curl", "-f", "http://127.0.0.1:8096"]
|
||||
interval: 2s
|
||||
timeout: 10s
|
||||
retries: 15
|
||||
|
||||
@@ -18,7 +18,7 @@ services:
|
||||
elasticsearch:
|
||||
image: kuzzleio/elasticsearch:7
|
||||
healthcheck:
|
||||
test: [ "CMD", "curl", "-f", "http://localhost:9200" ]
|
||||
test: [ "CMD", "curl", "-f", "http://127.0.0.1:9200" ]
|
||||
interval: 2s
|
||||
timeout: 2s
|
||||
retries: 10
|
||||
@@ -51,7 +51,7 @@ services:
|
||||
sysctls:
|
||||
- net.core.somaxconn=8192
|
||||
healthcheck:
|
||||
test: [ "CMD", "curl", "-f", "http://localhost:7512/_healthcheck" ]
|
||||
test: [ "CMD", "curl", "-f", "http://127.0.0.1:7512/_healthcheck" ]
|
||||
timeout: 1s
|
||||
interval: 2s
|
||||
retries: 30
|
||||
|
||||
@@ -18,7 +18,7 @@ services:
|
||||
- ENDPOINT=$LOGTO_ENDPOINT
|
||||
- ADMIN_ENDPOINT=$LOGTO_ADMIN_ENDPOINT
|
||||
healthcheck:
|
||||
test: ["CMD", "wget", "-q", "--spider", "http://localhost:3002"]
|
||||
test: ["CMD", "wget", "-q", "--spider", "http://127.0.0.1:3002"]
|
||||
interval: 5s
|
||||
timeout: 20s
|
||||
retries: 10
|
||||
|
||||
@@ -15,7 +15,7 @@ services:
|
||||
volumes:
|
||||
- meilisearch-data:/meili_data
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:7700/health"]
|
||||
test: ["CMD", "curl", "-f", "http://127.0.0.1:7700/health"]
|
||||
interval: 2s
|
||||
timeout: 10s
|
||||
retries: 15
|
||||
|
||||
@@ -18,7 +18,7 @@ services:
|
||||
- MB_DB_USER=$SERVICE_USER_POSTGRESQL
|
||||
- MB_DB_PASS=$SERVICE_PASSWORD_POSTGRESQL
|
||||
healthcheck:
|
||||
test: curl --fail -I http://localhost:3000/api/health || exit 1
|
||||
test: curl --fail -I http://127.0.0.1:3000/api/health || exit 1
|
||||
interval: 5s
|
||||
timeout: 20s
|
||||
retries: 10
|
||||
|
||||
@@ -13,7 +13,7 @@ services:
|
||||
volumes:
|
||||
- metube-downloads:/downloads
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:8081"]
|
||||
test: ["CMD", "curl", "-f", "http://127.0.0.1:8081"]
|
||||
interval: 2s
|
||||
timeout: 10s
|
||||
retries: 15
|
||||
|
||||
@@ -15,7 +15,7 @@ services:
|
||||
volumes:
|
||||
- minio-data:/data
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"]
|
||||
test: ["CMD", "curl", "-f", "http://127.0.0.1:9000/minio/health/live"]
|
||||
interval: 5s
|
||||
timeout: 20s
|
||||
retries: 10
|
||||
|
||||
@@ -27,7 +27,7 @@ services:
|
||||
postgresql:
|
||||
condition: service_healthy
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "wget -qO- http://localhost:5678/"]
|
||||
test: ["CMD-SHELL", "wget -qO- http://127.0.0.1:5678/"]
|
||||
interval: 5s
|
||||
timeout: 20s
|
||||
retries: 10
|
||||
|
||||
@@ -17,7 +17,7 @@ services:
|
||||
volumes:
|
||||
- n8n-data:/home/node/.n8n
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "wget -qO- http://localhost:5678/"]
|
||||
test: ["CMD-SHELL", "wget -qO- http://127.0.0.1:5678/"]
|
||||
interval: 5s
|
||||
timeout: 20s
|
||||
retries: 10
|
||||
|
||||
@@ -12,7 +12,7 @@ services:
|
||||
- ALLOWED_REMOTE_DOMAINS=${ALLOWED_REMOTE_DOMAINS:-*}
|
||||
- IMGPROXY_URL=${IMGPROXY_URL:-http://imgproxy:8080}
|
||||
healthcheck:
|
||||
test: "wget -qO- http://localhost:3000/health || exit 1"
|
||||
test: "wget -qO- http://127.0.0.1:3000/health || exit 1"
|
||||
interval: 2s
|
||||
timeout: 10s
|
||||
retries: 5
|
||||
|
||||
@@ -15,7 +15,7 @@ services:
|
||||
- nextcloud-config:/config
|
||||
- nextcloud-data:/data
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:80"]
|
||||
test: ["CMD", "curl", "-f", "http://127.0.0.1:80"]
|
||||
interval: 2s
|
||||
timeout: 10s
|
||||
retries: 15
|
||||
|
||||
@@ -12,7 +12,7 @@ services:
|
||||
volumes:
|
||||
- nocodb-data:/usr/app/data/
|
||||
healthcheck:
|
||||
test: ["CMD", "wget", "-q", "--spider", "http://localhost:8080"]
|
||||
test: ["CMD", "wget", "-q", "--spider", "http://127.0.0.1:8080"]
|
||||
interval: 5s
|
||||
timeout: 20s
|
||||
retries: 10
|
||||
|
||||
@@ -15,7 +15,7 @@ services:
|
||||
volumes:
|
||||
- odoo-web-data:/var/lib/odoo
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:8069"]
|
||||
test: ["CMD", "curl", "-f", "http://127.0.0.1:8069"]
|
||||
interval: 2s
|
||||
timeout: 10s
|
||||
retries: 30
|
||||
|
||||
@@ -15,7 +15,7 @@ services:
|
||||
volumes:
|
||||
- openblocks-data:/openblocks-stacks
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
|
||||
test: ["CMD", "curl", "-f", "http://127.0.0.1:3000/health"]
|
||||
interval: 5s
|
||||
timeout: 20s
|
||||
retries: 10
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user