@@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Actions\Proxy;
|
||||
|
||||
use App\Enums\ProxyTypes;
|
||||
use App\Events\ProxyStarted;
|
||||
use App\Models\Server;
|
||||
use Lorisleiva\Actions\Concerns\AsAction;
|
||||
@@ -37,11 +38,16 @@ class StartProxy
|
||||
"echo 'Successfully started coolify-proxy.'",
|
||||
]);
|
||||
} else {
|
||||
$caddfile = 'import /dynamic/*.caddy';
|
||||
if (isDev()) {
|
||||
if ($proxyType === ProxyTypes::CADDY->value) {
|
||||
$proxy_path = '/data/coolify/proxy/caddy';
|
||||
}
|
||||
}
|
||||
$caddyfile = 'import /dynamic/*.caddy';
|
||||
$commands = $commands->merge([
|
||||
"mkdir -p $proxy_path/dynamic",
|
||||
"cd $proxy_path",
|
||||
"echo '$caddfile' > $proxy_path/dynamic/Caddyfile",
|
||||
"echo '$caddyfile' > $proxy_path/dynamic/Caddyfile",
|
||||
"echo 'Creating required Docker Compose file.'",
|
||||
"echo 'Pulling docker image.'",
|
||||
'docker compose pull',
|
||||
|
||||
59
app/Jobs/SendMessageToSlackJob.php
Normal file
59
app/Jobs/SendMessageToSlackJob.php
Normal file
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
|
||||
namespace App\Jobs;
|
||||
|
||||
use App\Notifications\Dto\SlackMessage;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
|
||||
class SendMessageToSlackJob implements ShouldQueue
|
||||
{
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||
|
||||
public function __construct(
|
||||
private SlackMessage $message,
|
||||
private string $webhookUrl
|
||||
) {
|
||||
$this->onQueue('high');
|
||||
}
|
||||
|
||||
public function handle(): void
|
||||
{
|
||||
Http::post($this->webhookUrl, [
|
||||
'blocks' => [
|
||||
[
|
||||
'type' => 'section',
|
||||
'text' => [
|
||||
'type' => 'plain_text',
|
||||
'text' => 'Coolify Notification',
|
||||
],
|
||||
],
|
||||
],
|
||||
'attachments' => [
|
||||
[
|
||||
'color' => $this->message->color,
|
||||
'blocks' => [
|
||||
[
|
||||
'type' => 'header',
|
||||
'text' => [
|
||||
'type' => 'plain_text',
|
||||
'text' => $this->message->title,
|
||||
],
|
||||
],
|
||||
[
|
||||
'type' => 'section',
|
||||
'text' => [
|
||||
'type' => 'mrkdwn',
|
||||
'text' => $this->message->description,
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,7 @@ class ProxyStartedNotification
|
||||
public function handle(ProxyStarted $event): void
|
||||
{
|
||||
$this->server = data_get($event, 'data');
|
||||
$this->server->setupDefault404Redirect();
|
||||
$this->server->setupDefaultRedirect();
|
||||
$this->server->setupDynamicProxyConfiguration();
|
||||
$this->server->proxy->force_stop = false;
|
||||
$this->server->save();
|
||||
|
||||
130
app/Livewire/Notifications/Slack.php
Normal file
130
app/Livewire/Notifications/Slack.php
Normal file
@@ -0,0 +1,130 @@
|
||||
<?php
|
||||
|
||||
namespace App\Livewire\Notifications;
|
||||
|
||||
use App\Models\Team;
|
||||
use App\Notifications\Test;
|
||||
use Livewire\Attributes\Validate;
|
||||
use Livewire\Component;
|
||||
|
||||
class Slack extends Component
|
||||
{
|
||||
public Team $team;
|
||||
|
||||
#[Validate(['boolean'])]
|
||||
public bool $slackEnabled = false;
|
||||
|
||||
#[Validate(['url', 'nullable'])]
|
||||
public ?string $slackWebhookUrl = null;
|
||||
|
||||
#[Validate(['boolean'])]
|
||||
public bool $slackNotificationsTest = false;
|
||||
|
||||
#[Validate(['boolean'])]
|
||||
public bool $slackNotificationsDeployments = false;
|
||||
|
||||
#[Validate(['boolean'])]
|
||||
public bool $slackNotificationsStatusChanges = false;
|
||||
|
||||
#[Validate(['boolean'])]
|
||||
public bool $slackNotificationsDatabaseBackups = false;
|
||||
|
||||
#[Validate(['boolean'])]
|
||||
public bool $slackNotificationsScheduledTasks = false;
|
||||
|
||||
#[Validate(['boolean'])]
|
||||
public bool $slackNotificationsServerDiskUsage = false;
|
||||
|
||||
public function mount()
|
||||
{
|
||||
try {
|
||||
$this->team = auth()->user()->currentTeam();
|
||||
$this->syncData();
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
|
||||
public function syncData(bool $toModel = false)
|
||||
{
|
||||
if ($toModel) {
|
||||
$this->validate();
|
||||
$this->team->slack_enabled = $this->slackEnabled;
|
||||
$this->team->slack_webhook_url = $this->slackWebhookUrl;
|
||||
$this->team->slack_notifications_test = $this->slackNotificationsTest;
|
||||
$this->team->slack_notifications_deployments = $this->slackNotificationsDeployments;
|
||||
$this->team->slack_notifications_status_changes = $this->slackNotificationsStatusChanges;
|
||||
$this->team->slack_notifications_database_backups = $this->slackNotificationsDatabaseBackups;
|
||||
$this->team->slack_notifications_scheduled_tasks = $this->slackNotificationsScheduledTasks;
|
||||
$this->team->slack_notifications_server_disk_usage = $this->slackNotificationsServerDiskUsage;
|
||||
$this->team->save();
|
||||
refreshSession();
|
||||
} else {
|
||||
$this->slackEnabled = $this->team->slack_enabled;
|
||||
$this->slackWebhookUrl = $this->team->slack_webhook_url;
|
||||
$this->slackNotificationsTest = $this->team->slack_notifications_test;
|
||||
$this->slackNotificationsDeployments = $this->team->slack_notifications_deployments;
|
||||
$this->slackNotificationsStatusChanges = $this->team->slack_notifications_status_changes;
|
||||
$this->slackNotificationsDatabaseBackups = $this->team->slack_notifications_database_backups;
|
||||
$this->slackNotificationsScheduledTasks = $this->team->slack_notifications_scheduled_tasks;
|
||||
$this->slackNotificationsServerDiskUsage = $this->team->slack_notifications_server_disk_usage;
|
||||
}
|
||||
}
|
||||
|
||||
public function instantSaveSlackEnabled()
|
||||
{
|
||||
try {
|
||||
$this->validate([
|
||||
'slackWebhookUrl' => 'required',
|
||||
], [
|
||||
'slackWebhookUrl.required' => 'Slack Webhook URL is required.',
|
||||
]);
|
||||
$this->saveModel();
|
||||
} catch (\Throwable $e) {
|
||||
$this->slackEnabled = false;
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
|
||||
public function instantSave()
|
||||
{
|
||||
try {
|
||||
$this->syncData(true);
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
|
||||
public function submit()
|
||||
{
|
||||
try {
|
||||
$this->resetErrorBag();
|
||||
$this->syncData(true);
|
||||
$this->saveModel();
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
|
||||
public function saveModel()
|
||||
{
|
||||
$this->syncData(true);
|
||||
refreshSession();
|
||||
$this->dispatch('success', 'Settings saved.');
|
||||
}
|
||||
|
||||
public function sendTestNotification()
|
||||
{
|
||||
try {
|
||||
$this->team->notify(new Test);
|
||||
$this->dispatch('success', 'Test notification sent.');
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
|
||||
public function render()
|
||||
{
|
||||
return view('livewire.notifications.slack');
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,8 @@ class Proxy extends Component
|
||||
|
||||
public $proxy_settings = null;
|
||||
|
||||
public bool $redirect_enabled = true;
|
||||
|
||||
public ?string $redirect_url = null;
|
||||
|
||||
protected $listeners = ['proxyStatusUpdated', 'saveConfiguration' => 'submit'];
|
||||
@@ -26,6 +28,7 @@ class Proxy extends Component
|
||||
public function mount()
|
||||
{
|
||||
$this->selectedProxy = $this->server->proxyType();
|
||||
$this->redirect_enabled = data_get($this->server, 'proxy.redirect_enabled', true);
|
||||
$this->redirect_url = data_get($this->server, 'proxy.redirect_url');
|
||||
}
|
||||
|
||||
@@ -38,7 +41,7 @@ class Proxy extends Component
|
||||
{
|
||||
$this->server->proxy = null;
|
||||
$this->server->save();
|
||||
$this->dispatch('proxyChanged');
|
||||
$this->dispatch('reloadWindow');
|
||||
}
|
||||
|
||||
public function selectProxy($proxy_type)
|
||||
@@ -46,7 +49,7 @@ class Proxy extends Component
|
||||
try {
|
||||
$this->server->changeProxy($proxy_type, async: false);
|
||||
$this->selectedProxy = $this->server->proxy->type;
|
||||
$this->dispatch('proxyStatusUpdated');
|
||||
$this->dispatch('reloadWindow');
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
@@ -63,13 +66,25 @@ class Proxy extends Component
|
||||
}
|
||||
}
|
||||
|
||||
public function instantSaveRedirect()
|
||||
{
|
||||
try {
|
||||
$this->server->proxy->redirect_enabled = $this->redirect_enabled;
|
||||
$this->server->save();
|
||||
$this->server->setupDefaultRedirect();
|
||||
$this->dispatch('success', 'Proxy configuration saved.');
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
|
||||
public function submit()
|
||||
{
|
||||
try {
|
||||
SaveConfiguration::run($this->server, $this->proxy_settings);
|
||||
$this->server->proxy->redirect_url = $this->redirect_url;
|
||||
$this->server->save();
|
||||
$this->server->setupDefault404Redirect();
|
||||
$this->server->setupDefaultRedirect();
|
||||
$this->dispatch('success', 'Proxy configuration saved.');
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
|
||||
@@ -65,7 +65,7 @@ class Deploy extends Component
|
||||
public function restart()
|
||||
{
|
||||
try {
|
||||
$this->stop(forceStop: false);
|
||||
$this->stop();
|
||||
$this->dispatch('checkProxy');
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
@@ -105,6 +105,7 @@ class Deploy extends Component
|
||||
|
||||
$startTime = Carbon::now()->getTimestamp();
|
||||
while ($process->running()) {
|
||||
ray('running');
|
||||
if (Carbon::now()->getTimestamp() - $startTime >= $timeout) {
|
||||
$this->forceStopContainer($containerName);
|
||||
break;
|
||||
|
||||
@@ -1321,17 +1321,43 @@ class Application extends BaseModel
|
||||
if (! $gitRemoteStatus['is_accessible']) {
|
||||
throw new \RuntimeException("Failed to read Git source:\n\n{$gitRemoteStatus['error']}");
|
||||
}
|
||||
$getGitVersion = instant_remote_process(['git --version'], $this->destination->server, false);
|
||||
$gitVersion = str($getGitVersion)->explode(' ')->last();
|
||||
|
||||
$commands = collect([
|
||||
"rm -rf /tmp/{$uuid}",
|
||||
"mkdir -p /tmp/{$uuid}",
|
||||
"cd /tmp/{$uuid}",
|
||||
$cloneCommand,
|
||||
'git sparse-checkout init --cone',
|
||||
"git sparse-checkout set {$fileList->implode(' ')}",
|
||||
'git read-tree -mu HEAD',
|
||||
"cat .$workdir$composeFile",
|
||||
]);
|
||||
if (version_compare($gitVersion, '2.35.1', '<')) {
|
||||
$fileList = $fileList->map(function ($file) {
|
||||
$parts = explode('/', trim($file, '.'));
|
||||
$paths = collect();
|
||||
$currentPath = '';
|
||||
foreach ($parts as $part) {
|
||||
$currentPath .= ($currentPath ? '/' : '').$part;
|
||||
$paths->push($currentPath);
|
||||
}
|
||||
|
||||
return $paths;
|
||||
})->flatten()->unique()->values();
|
||||
$commands = collect([
|
||||
"rm -rf /tmp/{$uuid}",
|
||||
"mkdir -p /tmp/{$uuid}",
|
||||
"cd /tmp/{$uuid}",
|
||||
$cloneCommand,
|
||||
'git sparse-checkout init --cone',
|
||||
"git sparse-checkout set {$fileList->implode(' ')}",
|
||||
'git read-tree -mu HEAD',
|
||||
"cat .$workdir$composeFile",
|
||||
]);
|
||||
} else {
|
||||
$commands = collect([
|
||||
"rm -rf /tmp/{$uuid}",
|
||||
"mkdir -p /tmp/{$uuid}",
|
||||
"cd /tmp/{$uuid}",
|
||||
$cloneCommand,
|
||||
'git sparse-checkout init --cone',
|
||||
"git sparse-checkout set {$fileList->implode(' ')}",
|
||||
'git read-tree -mu HEAD',
|
||||
"cat .$workdir$composeFile",
|
||||
]);
|
||||
}
|
||||
try {
|
||||
$composeFileContent = instant_remote_process($commands, $this->destination->server);
|
||||
} catch (\Exception $e) {
|
||||
|
||||
@@ -105,6 +105,14 @@ class Server extends BaseModel
|
||||
]);
|
||||
}
|
||||
}
|
||||
if (! isset($server->proxy->redirect_enabled)) {
|
||||
$server->proxy->redirect_enabled = true;
|
||||
}
|
||||
});
|
||||
static::retrieved(function ($server) {
|
||||
if (! isset($server->proxy->redirect_enabled)) {
|
||||
$server->proxy->redirect_enabled = true;
|
||||
}
|
||||
});
|
||||
|
||||
static::forceDeleting(function ($server) {
|
||||
@@ -184,73 +192,80 @@ class Server extends BaseModel
|
||||
return $this->proxyType() && $this->proxyType() !== 'NONE' && $this->isFunctional() && ! $this->isSwarmWorker() && ! $this->settings->is_build_server;
|
||||
}
|
||||
|
||||
public function setupDefault404Redirect()
|
||||
public function setupDefaultRedirect()
|
||||
{
|
||||
$banner =
|
||||
"# This file is generated by Coolify, do not edit it manually.\n".
|
||||
"# Disable the default redirect to customize (only if you know what are you doing).\n\n";
|
||||
$dynamic_conf_path = $this->proxyPath().'/dynamic';
|
||||
$proxy_type = $this->proxyType();
|
||||
$redirect_enabled = $this->proxy->redirect_enabled ?? true;
|
||||
$redirect_url = $this->proxy->redirect_url;
|
||||
if ($proxy_type === ProxyTypes::TRAEFIK->value) {
|
||||
$default_redirect_file = "$dynamic_conf_path/default_redirect_404.yaml";
|
||||
} elseif ($proxy_type === ProxyTypes::CADDY->value) {
|
||||
$default_redirect_file = "$dynamic_conf_path/default_redirect_404.caddy";
|
||||
}
|
||||
if (empty($redirect_url)) {
|
||||
if (isDev()) {
|
||||
if ($proxy_type === ProxyTypes::CADDY->value) {
|
||||
$conf = ':80, :443 {
|
||||
respond 404
|
||||
}';
|
||||
$conf =
|
||||
"# This file is automatically generated by Coolify.\n".
|
||||
"# Do not edit it manually (only if you know what are you doing).\n\n".
|
||||
$conf;
|
||||
$base64 = base64_encode($conf);
|
||||
instant_remote_process([
|
||||
"mkdir -p $dynamic_conf_path",
|
||||
"echo '$base64' | base64 -d | tee $default_redirect_file > /dev/null",
|
||||
], $this);
|
||||
$this->reloadCaddy();
|
||||
|
||||
return;
|
||||
$dynamic_conf_path = '/data/coolify/proxy/caddy/dynamic';
|
||||
}
|
||||
instant_remote_process([
|
||||
"mkdir -p $dynamic_conf_path",
|
||||
"rm -f $default_redirect_file",
|
||||
], $this);
|
||||
|
||||
return;
|
||||
}
|
||||
if ($proxy_type === ProxyTypes::TRAEFIK->value) {
|
||||
$dynamic_conf = [
|
||||
'http' => [
|
||||
'routers' => [
|
||||
'catchall' => [
|
||||
'entryPoints' => [
|
||||
0 => 'http',
|
||||
1 => 'https',
|
||||
],
|
||||
'service' => 'noop',
|
||||
'rule' => 'HostRegexp(`.+`)',
|
||||
'tls' => [
|
||||
'certResolver' => 'letsencrypt',
|
||||
],
|
||||
'priority' => 1,
|
||||
'middlewares' => [
|
||||
0 => 'redirect-regexp',
|
||||
$default_redirect_file = "$dynamic_conf_path/default_redirect_503.yaml";
|
||||
} elseif ($proxy_type === ProxyTypes::CADDY->value) {
|
||||
$default_redirect_file = "$dynamic_conf_path/default_redirect_503.caddy";
|
||||
}
|
||||
|
||||
instant_remote_process([
|
||||
"mkdir -p $dynamic_conf_path",
|
||||
"rm -f $dynamic_conf_path/default_redirect_404.yaml",
|
||||
"rm -f $dynamic_conf_path/default_redirect_404.caddy",
|
||||
], $this);
|
||||
|
||||
if ($redirect_enabled === false) {
|
||||
instant_remote_process(["rm -f $default_redirect_file"], $this);
|
||||
} else {
|
||||
if ($proxy_type === ProxyTypes::CADDY->value) {
|
||||
if (filled($redirect_url)) {
|
||||
$conf = ":80, :443 {
|
||||
redir $redirect_url
|
||||
}";
|
||||
} else {
|
||||
$conf = ':80, :443 {
|
||||
respond 503
|
||||
}';
|
||||
}
|
||||
} elseif ($proxy_type === ProxyTypes::TRAEFIK->value) {
|
||||
$dynamic_conf = [
|
||||
'http' => [
|
||||
'routers' => [
|
||||
'catchall' => [
|
||||
'entryPoints' => [
|
||||
0 => 'http',
|
||||
1 => 'https',
|
||||
],
|
||||
'service' => 'noop',
|
||||
'rule' => 'PathPrefix(`/`)',
|
||||
'tls' => [
|
||||
'certResolver' => 'letsencrypt',
|
||||
],
|
||||
'priority' => -1000,
|
||||
],
|
||||
],
|
||||
],
|
||||
'services' => [
|
||||
'noop' => [
|
||||
'loadBalancer' => [
|
||||
'servers' => [
|
||||
0 => [
|
||||
'url' => '',
|
||||
],
|
||||
'services' => [
|
||||
'noop' => [
|
||||
'loadBalancer' => [
|
||||
'servers' => [],
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
'middlewares' => [
|
||||
];
|
||||
if (filled($redirect_url)) {
|
||||
$dynamic_conf['http']['routers']['catchall']['middlewares'] = [
|
||||
0 => 'redirect-regexp',
|
||||
];
|
||||
|
||||
$dynamic_conf['http']['services']['noop']['loadBalancer']['servers'][0] = [
|
||||
'url' => '',
|
||||
];
|
||||
$dynamic_conf['http']['middlewares'] = [
|
||||
'redirect-regexp' => [
|
||||
'redirectRegex' => [
|
||||
'regex' => '(.*)',
|
||||
@@ -258,32 +273,17 @@ respond 404
|
||||
'permanent' => false,
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
$conf = Yaml::dump($dynamic_conf, 12, 2);
|
||||
$conf =
|
||||
"# This file is automatically generated by Coolify.\n".
|
||||
"# Do not edit it manually (only if you know what are you doing).\n\n".
|
||||
$conf;
|
||||
|
||||
$base64 = base64_encode($conf);
|
||||
} elseif ($proxy_type === ProxyTypes::CADDY->value) {
|
||||
$conf = ":80, :443 {
|
||||
redir $redirect_url
|
||||
}";
|
||||
$conf =
|
||||
"# This file is automatically generated by Coolify.\n".
|
||||
"# Do not edit it manually (only if you know what are you doing).\n\n".
|
||||
$conf;
|
||||
];
|
||||
}
|
||||
$conf = Yaml::dump($dynamic_conf, 12, 2);
|
||||
}
|
||||
$conf = $banner.$conf;
|
||||
$base64 = base64_encode($conf);
|
||||
instant_remote_process([
|
||||
"echo '$base64' | base64 -d | tee $default_redirect_file > /dev/null",
|
||||
], $this);
|
||||
}
|
||||
|
||||
instant_remote_process([
|
||||
"mkdir -p $dynamic_conf_path",
|
||||
"echo '$base64' | base64 -d | tee $default_redirect_file > /dev/null",
|
||||
], $this);
|
||||
|
||||
if ($proxy_type === 'CADDY') {
|
||||
$this->reloadCaddy();
|
||||
}
|
||||
@@ -612,6 +612,7 @@ $schema://$host {
|
||||
$memory = json_decode($memory, true);
|
||||
$parsedCollection = collect($memory)->map(function ($metric) {
|
||||
$usedPercent = $metric['usedPercent'] ?? 0.0;
|
||||
|
||||
return [(int) $metric['time'], (float) $usedPercent];
|
||||
});
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ namespace App\Models;
|
||||
|
||||
use App\Notifications\Channels\SendsDiscord;
|
||||
use App\Notifications\Channels\SendsEmail;
|
||||
use App\Notifications\Channels\SendsSlack;
|
||||
use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Notifications\Notifiable;
|
||||
@@ -70,7 +71,7 @@ use OpenApi\Attributes as OA;
|
||||
),
|
||||
]
|
||||
)]
|
||||
class Team extends Model implements SendsDiscord, SendsEmail
|
||||
class Team extends Model implements SendsDiscord, SendsEmail, SendsSlack
|
||||
{
|
||||
use Notifiable;
|
||||
|
||||
@@ -127,6 +128,11 @@ class Team extends Model implements SendsDiscord, SendsEmail
|
||||
];
|
||||
}
|
||||
|
||||
public function routeNotificationForSlack()
|
||||
{
|
||||
return data_get($this, 'slack_webhook_url', null);
|
||||
}
|
||||
|
||||
public function getRecepients($notification)
|
||||
{
|
||||
$recipients = data_get($notification, 'emails', null);
|
||||
@@ -161,7 +167,7 @@ class Team extends Model implements SendsDiscord, SendsEmail
|
||||
return 9999999;
|
||||
}
|
||||
$team = Team::find(currentTeam()->id);
|
||||
if (! $team) {
|
||||
if (!$team) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ use App\Models\Application;
|
||||
use App\Models\ApplicationPreview;
|
||||
use App\Notifications\CustomEmailNotification;
|
||||
use App\Notifications\Dto\DiscordMessage;
|
||||
use App\Notifications\Dto\SlackMessage;
|
||||
use Illuminate\Notifications\Messages\MailMessage;
|
||||
|
||||
class DeploymentFailed extends CustomEmailNotification
|
||||
@@ -39,7 +40,7 @@ class DeploymentFailed extends CustomEmailNotification
|
||||
if (str($this->fqdn)->explode(',')->count() > 1) {
|
||||
$this->fqdn = str($this->fqdn)->explode(',')->first();
|
||||
}
|
||||
$this->deployment_url = base_url()."/project/{$this->project_uuid}/".urlencode($this->environment_name)."/application/{$this->application->uuid}/deployment/{$this->deployment_uuid}";
|
||||
$this->deployment_url = base_url() . "/project/{$this->project_uuid}/" . urlencode($this->environment_name) . "/application/{$this->application->uuid}/deployment/{$this->deployment_uuid}";
|
||||
}
|
||||
|
||||
public function via(object $notifiable): array
|
||||
@@ -53,10 +54,10 @@ class DeploymentFailed extends CustomEmailNotification
|
||||
$pull_request_id = data_get($this->preview, 'pull_request_id', 0);
|
||||
$fqdn = $this->fqdn;
|
||||
if ($pull_request_id === 0) {
|
||||
$mail->subject('Coolify: Deployment failed of '.$this->application_name.'.');
|
||||
$mail->subject('Coolify: Deployment failed of ' . $this->application_name . '.');
|
||||
} else {
|
||||
$fqdn = $this->preview->fqdn;
|
||||
$mail->subject('Coolify: Deployment failed of pull request #'.$this->preview->pull_request_id.' of '.$this->application_name.'.');
|
||||
$mail->subject('Coolify: Deployment failed of pull request #' . $this->preview->pull_request_id . ' of ' . $this->application_name . '.');
|
||||
}
|
||||
$mail->view('emails.application-deployment-failed', [
|
||||
'name' => $this->application_name,
|
||||
@@ -73,7 +74,7 @@ class DeploymentFailed extends CustomEmailNotification
|
||||
if ($this->preview) {
|
||||
$message = new DiscordMessage(
|
||||
title: ':cross_mark: Deployment failed',
|
||||
description: 'Pull request: '.$this->preview->pull_request_id,
|
||||
description: 'Pull request: ' . $this->preview->pull_request_id,
|
||||
color: DiscordMessage::errorColor(),
|
||||
isCritical: true,
|
||||
);
|
||||
@@ -82,13 +83,13 @@ class DeploymentFailed extends CustomEmailNotification
|
||||
$message->addField('Environment', $this->environment_name, true);
|
||||
$message->addField('Name', $this->application_name, true);
|
||||
|
||||
$message->addField('Deployment Logs', '[Link]('.$this->deployment_url.')');
|
||||
$message->addField('Deployment Logs', '[Link](' . $this->deployment_url . ')');
|
||||
if ($this->fqdn) {
|
||||
$message->addField('Domain', $this->fqdn, true);
|
||||
}
|
||||
} else {
|
||||
if ($this->fqdn) {
|
||||
$description = '[Open application]('.$this->fqdn.')';
|
||||
$description = '[Open application](' . $this->fqdn . ')';
|
||||
} else {
|
||||
$description = '';
|
||||
}
|
||||
@@ -103,7 +104,7 @@ class DeploymentFailed extends CustomEmailNotification
|
||||
$message->addField('Environment', $this->environment_name, true);
|
||||
$message->addField('Name', $this->application_name, true);
|
||||
|
||||
$message->addField('Deployment Logs', '[Link]('.$this->deployment_url.')');
|
||||
$message->addField('Deployment Logs', '[Link](' . $this->deployment_url . ')');
|
||||
}
|
||||
|
||||
return $message;
|
||||
@@ -112,9 +113,9 @@ class DeploymentFailed extends CustomEmailNotification
|
||||
public function toTelegram(): array
|
||||
{
|
||||
if ($this->preview) {
|
||||
$message = 'Coolify: Pull request #'.$this->preview->pull_request_id.' of '.$this->application_name.' ('.$this->preview->fqdn.') deployment failed: ';
|
||||
$message = 'Coolify: Pull request #' . $this->preview->pull_request_id . ' of ' . $this->application_name . ' (' . $this->preview->fqdn . ') deployment failed: ';
|
||||
} else {
|
||||
$message = 'Coolify: Deployment failed of '.$this->application_name.' ('.$this->fqdn.'): ';
|
||||
$message = 'Coolify: Deployment failed of ' . $this->application_name . ' (' . $this->fqdn . '): ';
|
||||
}
|
||||
$buttons[] = [
|
||||
'text' => 'Deployment logs',
|
||||
@@ -128,4 +129,31 @@ class DeploymentFailed extends CustomEmailNotification
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
public function toSlack(): SlackMessage
|
||||
{
|
||||
if ($this->preview) {
|
||||
$title = "Pull request #{$this->preview->pull_request_id} deployment failed";
|
||||
$description = "Pull request deployment failed for {$this->application_name}";
|
||||
if ($this->preview->fqdn) {
|
||||
$description .= "\nPreview URL: {$this->preview->fqdn}";
|
||||
}
|
||||
} else {
|
||||
$title = "Deployment failed";
|
||||
$description = "Deployment failed for {$this->application_name}";
|
||||
if ($this->fqdn) {
|
||||
$description .= "\nApplication URL: {$this->fqdn}";
|
||||
}
|
||||
}
|
||||
|
||||
$description .= "\n\n**Project:** " . data_get($this->application, 'environment.project.name');
|
||||
$description .= "\n**Environment:** {$this->environment_name}";
|
||||
$description .= "\n**Deployment Logs:** {$this->deployment_url}";
|
||||
|
||||
return new SlackMessage(
|
||||
title: $title,
|
||||
description: $description,
|
||||
color: SlackMessage::errorColor()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ use App\Models\ApplicationPreview;
|
||||
use App\Notifications\CustomEmailNotification;
|
||||
use App\Notifications\Dto\DiscordMessage;
|
||||
use Illuminate\Notifications\Messages\MailMessage;
|
||||
use App\Notifications\Dto\SlackMessage;
|
||||
|
||||
class DeploymentSuccess extends CustomEmailNotification
|
||||
{
|
||||
@@ -39,7 +40,7 @@ class DeploymentSuccess extends CustomEmailNotification
|
||||
if (str($this->fqdn)->explode(',')->count() > 1) {
|
||||
$this->fqdn = str($this->fqdn)->explode(',')->first();
|
||||
}
|
||||
$this->deployment_url = base_url()."/project/{$this->project_uuid}/".urlencode($this->environment_name)."/application/{$this->application->uuid}/deployment/{$this->deployment_uuid}";
|
||||
$this->deployment_url = base_url() . "/project/{$this->project_uuid}/" . urlencode($this->environment_name) . "/application/{$this->application->uuid}/deployment/{$this->deployment_uuid}";
|
||||
}
|
||||
|
||||
public function via(object $notifiable): array
|
||||
@@ -79,21 +80,21 @@ class DeploymentSuccess extends CustomEmailNotification
|
||||
if ($this->preview) {
|
||||
$message = new DiscordMessage(
|
||||
title: ':white_check_mark: Preview deployment successful',
|
||||
description: 'Pull request: '.$this->preview->pull_request_id,
|
||||
description: 'Pull request: ' . $this->preview->pull_request_id,
|
||||
color: DiscordMessage::successColor(),
|
||||
);
|
||||
|
||||
if ($this->preview->fqdn) {
|
||||
$message->addField('Application', '[Link]('.$this->preview->fqdn.')');
|
||||
$message->addField('Application', '[Link](' . $this->preview->fqdn . ')');
|
||||
}
|
||||
|
||||
$message->addField('Project', data_get($this->application, 'environment.project.name'), true);
|
||||
$message->addField('Environment', $this->environment_name, true);
|
||||
$message->addField('Name', $this->application_name, true);
|
||||
$message->addField('Deployment logs', '[Link]('.$this->deployment_url.')');
|
||||
$message->addField('Deployment logs', '[Link](' . $this->deployment_url . ')');
|
||||
} else {
|
||||
if ($this->fqdn) {
|
||||
$description = '[Open application]('.$this->fqdn.')';
|
||||
$description = '[Open application](' . $this->fqdn . ')';
|
||||
} else {
|
||||
$description = '';
|
||||
}
|
||||
@@ -106,7 +107,7 @@ class DeploymentSuccess extends CustomEmailNotification
|
||||
$message->addField('Environment', $this->environment_name, true);
|
||||
$message->addField('Name', $this->application_name, true);
|
||||
|
||||
$message->addField('Deployment logs', '[Link]('.$this->deployment_url.')');
|
||||
$message->addField('Deployment logs', '[Link](' . $this->deployment_url . ')');
|
||||
}
|
||||
|
||||
return $message;
|
||||
@@ -115,7 +116,7 @@ class DeploymentSuccess extends CustomEmailNotification
|
||||
public function toTelegram(): array
|
||||
{
|
||||
if ($this->preview) {
|
||||
$message = 'Coolify: New PR'.$this->preview->pull_request_id.' version successfully deployed of '.$this->application_name.'';
|
||||
$message = 'Coolify: New PR' . $this->preview->pull_request_id . ' version successfully deployed of ' . $this->application_name . '';
|
||||
if ($this->preview->fqdn) {
|
||||
$buttons[] = [
|
||||
'text' => 'Open Application',
|
||||
@@ -123,7 +124,7 @@ class DeploymentSuccess extends CustomEmailNotification
|
||||
];
|
||||
}
|
||||
} else {
|
||||
$message = '✅ New version successfully deployed of '.$this->application_name.'';
|
||||
$message = '✅ New version successfully deployed of ' . $this->application_name . '';
|
||||
if ($this->fqdn) {
|
||||
$buttons[] = [
|
||||
'text' => 'Open Application',
|
||||
@@ -143,4 +144,32 @@ class DeploymentSuccess extends CustomEmailNotification
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
public function toSlack(): SlackMessage
|
||||
{
|
||||
if ($this->preview) {
|
||||
$title = "Pull request #{$this->preview->pull_request_id} successfully deployed";
|
||||
$description = "New version successfully deployed for {$this->application_name}";
|
||||
if ($this->preview->fqdn) {
|
||||
$description .= "\nPreview URL: {$this->preview->fqdn}";
|
||||
}
|
||||
} else {
|
||||
$title = "New version successfully deployed";
|
||||
$description = "New version successfully deployed for {$this->application_name}";
|
||||
if ($this->fqdn) {
|
||||
$description .= "\nApplication URL: {$this->fqdn}";
|
||||
}
|
||||
}
|
||||
|
||||
$description .= "\n\n**Project:** " . data_get($this->application, 'environment.project.name');
|
||||
$description .= "\n**Environment:** {$this->environment_name}";
|
||||
$description .= "\n**Deployment Logs:** {$this->deployment_url}";
|
||||
|
||||
return new SlackMessage(
|
||||
title: $title,
|
||||
description: $description,
|
||||
color: SlackMessage::successColor()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ use App\Models\Application;
|
||||
use App\Notifications\CustomEmailNotification;
|
||||
use App\Notifications\Dto\DiscordMessage;
|
||||
use Illuminate\Notifications\Messages\MailMessage;
|
||||
use App\Notifications\Dto\SlackMessage;
|
||||
|
||||
class StatusChanged extends CustomEmailNotification
|
||||
{
|
||||
@@ -29,7 +30,7 @@ class StatusChanged extends CustomEmailNotification
|
||||
if (str($this->fqdn)->explode(',')->count() > 1) {
|
||||
$this->fqdn = str($this->fqdn)->explode(',')->first();
|
||||
}
|
||||
$this->resource_url = base_url()."/project/{$this->project_uuid}/".urlencode($this->environment_name)."/application/{$this->resource->uuid}";
|
||||
$this->resource_url = base_url() . "/project/{$this->project_uuid}/" . urlencode($this->environment_name) . "/application/{$this->resource->uuid}";
|
||||
}
|
||||
|
||||
public function via(object $notifiable): array
|
||||
@@ -55,7 +56,7 @@ class StatusChanged extends CustomEmailNotification
|
||||
{
|
||||
return new DiscordMessage(
|
||||
title: ':cross_mark: Application stopped',
|
||||
description: '[Open Application in Coolify]('.$this->resource_url.')',
|
||||
description: '[Open Application in Coolify](' . $this->resource_url . ')',
|
||||
color: DiscordMessage::errorColor(),
|
||||
isCritical: true,
|
||||
);
|
||||
@@ -63,7 +64,7 @@ class StatusChanged extends CustomEmailNotification
|
||||
|
||||
public function toTelegram(): array
|
||||
{
|
||||
$message = 'Coolify: '.$this->resource_name.' has been stopped.';
|
||||
$message = 'Coolify: ' . $this->resource_name . ' has been stopped.';
|
||||
|
||||
return [
|
||||
'message' => $message,
|
||||
@@ -75,4 +76,20 @@ class StatusChanged extends CustomEmailNotification
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
public function toSlack(): SlackMessage
|
||||
{
|
||||
$title = "Application stopped";
|
||||
$description = "{$this->resource_name} has been stopped";
|
||||
|
||||
$description .= "\n\n**Project:** " . data_get($this->resource, 'environment.project.name');
|
||||
$description .= "\n**Environment:** {$this->environment_name}";
|
||||
$description .= "\n**Application URL:** {$this->resource_url}";
|
||||
|
||||
return new SlackMessage(
|
||||
title: $title,
|
||||
description: $description,
|
||||
color: SlackMessage::errorColor()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
8
app/Notifications/Channels/SendsSlack.php
Normal file
8
app/Notifications/Channels/SendsSlack.php
Normal file
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
|
||||
namespace App\Notifications\Channels;
|
||||
|
||||
interface SendsSlack
|
||||
{
|
||||
public function routeNotificationForSlack();
|
||||
}
|
||||
22
app/Notifications/Channels/SlackChannel.php
Normal file
22
app/Notifications/Channels/SlackChannel.php
Normal file
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
namespace App\Notifications\Channels;
|
||||
|
||||
use App\Jobs\SendMessageToSlackJob;
|
||||
use Illuminate\Notifications\Notification;
|
||||
|
||||
class SlackChannel
|
||||
{
|
||||
/**
|
||||
* Send the given notification.
|
||||
*/
|
||||
public function send(SendsSlack $notifiable, Notification $notification): void
|
||||
{
|
||||
$message = $notification->toSlack();
|
||||
$webhookUrl = $notifiable->routeNotificationForSlack();
|
||||
if (! $webhookUrl) {
|
||||
return;
|
||||
}
|
||||
SendMessageToSlackJob::dispatch($message, $webhookUrl);
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@ use App\Models\Server;
|
||||
use App\Notifications\CustomEmailNotification;
|
||||
use App\Notifications\Dto\DiscordMessage;
|
||||
use Illuminate\Notifications\Messages\MailMessage;
|
||||
use App\Notifications\Dto\SlackMessage;
|
||||
|
||||
class ContainerRestarted extends CustomEmailNotification
|
||||
{
|
||||
@@ -41,7 +42,7 @@ class ContainerRestarted extends CustomEmailNotification
|
||||
);
|
||||
|
||||
if ($this->url) {
|
||||
$message->addField('Resource', '[Link]('.$this->url.')');
|
||||
$message->addField('Resource', '[Link](' . $this->url . ')');
|
||||
}
|
||||
|
||||
return $message;
|
||||
@@ -66,4 +67,20 @@ class ContainerRestarted extends CustomEmailNotification
|
||||
|
||||
return $payload;
|
||||
}
|
||||
|
||||
public function toSlack(): SlackMessage
|
||||
{
|
||||
$title = "Resource restarted";
|
||||
$description = "A resource ({$this->name}) has been restarted automatically on {$this->server->name}";
|
||||
|
||||
if ($this->url) {
|
||||
$description .= "\n**Resource URL:** {$this->url}";
|
||||
}
|
||||
|
||||
return new SlackMessage(
|
||||
title: $title,
|
||||
description: $description,
|
||||
color: SlackMessage::warningColor()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ use App\Models\Server;
|
||||
use App\Notifications\CustomEmailNotification;
|
||||
use App\Notifications\Dto\DiscordMessage;
|
||||
use Illuminate\Notifications\Messages\MailMessage;
|
||||
use App\Notifications\Dto\SlackMessage;
|
||||
|
||||
class ContainerStopped extends CustomEmailNotification
|
||||
{
|
||||
@@ -41,7 +42,7 @@ class ContainerStopped extends CustomEmailNotification
|
||||
);
|
||||
|
||||
if ($this->url) {
|
||||
$message->addField('Resource', '[Link]('.$this->url.')');
|
||||
$message->addField('Resource', '[Link](' . $this->url . ')');
|
||||
}
|
||||
|
||||
return $message;
|
||||
@@ -66,4 +67,20 @@ class ContainerStopped extends CustomEmailNotification
|
||||
|
||||
return $payload;
|
||||
}
|
||||
|
||||
public function toSlack(): SlackMessage
|
||||
{
|
||||
$title = "Resource stopped";
|
||||
$description = "A resource ({$this->name}) has been stopped unexpectedly on {$this->server->name}";
|
||||
|
||||
if ($this->url) {
|
||||
$description .= "\n**Resource URL:** {$this->url}";
|
||||
}
|
||||
|
||||
return new SlackMessage(
|
||||
title: $title,
|
||||
description: $description,
|
||||
color: SlackMessage::errorColor()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ use App\Models\ScheduledDatabaseBackup;
|
||||
use App\Notifications\CustomEmailNotification;
|
||||
use App\Notifications\Dto\DiscordMessage;
|
||||
use Illuminate\Notifications\Messages\MailMessage;
|
||||
use App\Notifications\Dto\SlackMessage;
|
||||
|
||||
class BackupFailed extends CustomEmailNotification
|
||||
{
|
||||
@@ -62,4 +63,19 @@ class BackupFailed extends CustomEmailNotification
|
||||
'message' => $message,
|
||||
];
|
||||
}
|
||||
|
||||
public function toSlack(): SlackMessage
|
||||
{
|
||||
$title = "Database backup failed";
|
||||
$description = "Database backup for {$this->name} (db:{$this->database_name}) has FAILED.";
|
||||
|
||||
$description .= "\n\n**Frequency:** {$this->frequency}";
|
||||
$description .= "\n\n**Error Output:**\n{$this->output}";
|
||||
|
||||
return new SlackMessage(
|
||||
title: $title,
|
||||
description: $description,
|
||||
color: SlackMessage::errorColor()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ use App\Models\ScheduledDatabaseBackup;
|
||||
use App\Notifications\CustomEmailNotification;
|
||||
use App\Notifications\Dto\DiscordMessage;
|
||||
use Illuminate\Notifications\Messages\MailMessage;
|
||||
use App\Notifications\Dto\SlackMessage;
|
||||
|
||||
class BackupSuccess extends CustomEmailNotification
|
||||
{
|
||||
@@ -60,4 +61,18 @@ class BackupSuccess extends CustomEmailNotification
|
||||
'message' => $message,
|
||||
];
|
||||
}
|
||||
|
||||
public function toSlack(): SlackMessage
|
||||
{
|
||||
$title = "Database backup successful";
|
||||
$description = "Database backup for {$this->name} (db:{$this->database_name}) was successful.";
|
||||
|
||||
$description .= "\n\n**Frequency:** {$this->frequency}";
|
||||
|
||||
return new SlackMessage(
|
||||
title: $title,
|
||||
description: $description,
|
||||
color: SlackMessage::successColor()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
33
app/Notifications/Dto/SlackMessage.php
Normal file
33
app/Notifications/Dto/SlackMessage.php
Normal file
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
namespace App\Notifications\Dto;
|
||||
|
||||
class SlackMessage
|
||||
{
|
||||
public function __construct(
|
||||
public string $title,
|
||||
public string $description,
|
||||
public string $color = '#0099ff'
|
||||
) {
|
||||
}
|
||||
|
||||
public static function infoColor(): string
|
||||
{
|
||||
return '#0099ff';
|
||||
}
|
||||
|
||||
public static function errorColor(): string
|
||||
{
|
||||
return '#ff0000';
|
||||
}
|
||||
|
||||
public static function successColor(): string
|
||||
{
|
||||
return '#00ff00';
|
||||
}
|
||||
|
||||
public static function warningColor(): string
|
||||
{
|
||||
return '#ffa500';
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,9 @@ namespace App\Notifications\Internal;
|
||||
|
||||
use App\Notifications\Channels\DiscordChannel;
|
||||
use App\Notifications\Channels\TelegramChannel;
|
||||
use App\Notifications\Channels\SlackChannel;
|
||||
use App\Notifications\Dto\DiscordMessage;
|
||||
use App\Notifications\Dto\SlackMessage;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Notifications\Notification;
|
||||
@@ -25,6 +27,7 @@ class GeneralNotification extends Notification implements ShouldQueue
|
||||
$channels = [];
|
||||
$isDiscordEnabled = data_get($notifiable, 'discord_enabled');
|
||||
$isTelegramEnabled = data_get($notifiable, 'telegram_enabled');
|
||||
$isSlackEnabled = data_get($notifiable, 'slack_enabled');
|
||||
|
||||
if ($isDiscordEnabled) {
|
||||
$channels[] = DiscordChannel::class;
|
||||
@@ -32,6 +35,9 @@ class GeneralNotification extends Notification implements ShouldQueue
|
||||
if ($isTelegramEnabled) {
|
||||
$channels[] = TelegramChannel::class;
|
||||
}
|
||||
if ($isSlackEnabled) {
|
||||
$channels[] = SlackChannel::class;
|
||||
}
|
||||
|
||||
return $channels;
|
||||
}
|
||||
@@ -51,4 +57,13 @@ class GeneralNotification extends Notification implements ShouldQueue
|
||||
'message' => $this->message,
|
||||
];
|
||||
}
|
||||
|
||||
public function toSlack(): SlackMessage
|
||||
{
|
||||
return new SlackMessage(
|
||||
title: 'Coolify: General Notification',
|
||||
description: $this->message,
|
||||
color: SlackMessage::infoColor(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ use App\Models\ScheduledTask;
|
||||
use App\Notifications\CustomEmailNotification;
|
||||
use App\Notifications\Dto\DiscordMessage;
|
||||
use Illuminate\Notifications\Messages\MailMessage;
|
||||
use App\Notifications\Dto\SlackMessage;
|
||||
|
||||
class TaskFailed extends CustomEmailNotification
|
||||
{
|
||||
@@ -48,7 +49,7 @@ class TaskFailed extends CustomEmailNotification
|
||||
);
|
||||
|
||||
if ($this->url) {
|
||||
$message->addField('Scheduled task', '[Link]('.$this->url.')');
|
||||
$message->addField('Scheduled task', '[Link](' . $this->url . ')');
|
||||
}
|
||||
|
||||
return $message;
|
||||
@@ -68,4 +69,24 @@ class TaskFailed extends CustomEmailNotification
|
||||
'message' => $message,
|
||||
];
|
||||
}
|
||||
|
||||
public function toSlack(): SlackMessage
|
||||
{
|
||||
$title = "Scheduled task failed";
|
||||
$description = "Scheduled task ({$this->task->name}) failed.";
|
||||
|
||||
if ($this->output) {
|
||||
$description .= "\n\n**Error Output:**\n{$this->output}";
|
||||
}
|
||||
|
||||
if ($this->url) {
|
||||
$description .= "\n\n**Task URL:** {$this->url}";
|
||||
}
|
||||
|
||||
return new SlackMessage(
|
||||
title: $title,
|
||||
description: $description,
|
||||
color: SlackMessage::errorColor()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ use App\Notifications\Channels\DiscordChannel;
|
||||
use App\Notifications\Channels\TelegramChannel;
|
||||
use App\Notifications\CustomEmailNotification;
|
||||
use App\Notifications\Dto\DiscordMessage;
|
||||
use App\Notifications\Dto\SlackMessage;
|
||||
|
||||
class DockerCleanup extends CustomEmailNotification
|
||||
{
|
||||
@@ -21,7 +22,7 @@ class DockerCleanup extends CustomEmailNotification
|
||||
// $isEmailEnabled = isEmailEnabled($notifiable);
|
||||
$isDiscordEnabled = data_get($notifiable, 'discord_enabled');
|
||||
$isTelegramEnabled = data_get($notifiable, 'telegram_enabled');
|
||||
|
||||
$isSlackEnabled = data_get($notifiable, 'slack_enabled');
|
||||
if ($isDiscordEnabled) {
|
||||
$channels[] = DiscordChannel::class;
|
||||
}
|
||||
@@ -31,6 +32,9 @@ class DockerCleanup extends CustomEmailNotification
|
||||
if ($isTelegramEnabled) {
|
||||
$channels[] = TelegramChannel::class;
|
||||
}
|
||||
if ($isSlackEnabled) {
|
||||
$channels[] = SlackChannel::class;
|
||||
}
|
||||
|
||||
return $channels;
|
||||
}
|
||||
@@ -62,4 +66,13 @@ class DockerCleanup extends CustomEmailNotification
|
||||
'message' => "Coolify: Server '{$this->server->name}' cleanup job done!\n\n{$this->message}",
|
||||
];
|
||||
}
|
||||
|
||||
public function toSlack(): SlackMessage
|
||||
{
|
||||
return new SlackMessage(
|
||||
title: 'Server cleanup job done',
|
||||
description: "Server '{$this->server->name}' cleanup job done!\n\n{$this->message}",
|
||||
color: SlackMessage::successColor()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,8 @@ use App\Models\Server;
|
||||
use App\Notifications\Channels\DiscordChannel;
|
||||
use App\Notifications\Channels\EmailChannel;
|
||||
use App\Notifications\Channels\TelegramChannel;
|
||||
use App\Notifications\Channels\SlackChannel;
|
||||
use App\Notifications\Dto\SlackMessage;
|
||||
use App\Notifications\CustomEmailNotification;
|
||||
use App\Notifications\Dto\DiscordMessage;
|
||||
use Illuminate\Notifications\Messages\MailMessage;
|
||||
@@ -23,7 +25,7 @@ class ForceDisabled extends CustomEmailNotification
|
||||
$isEmailEnabled = isEmailEnabled($notifiable);
|
||||
$isDiscordEnabled = data_get($notifiable, 'discord_enabled');
|
||||
$isTelegramEnabled = data_get($notifiable, 'telegram_enabled');
|
||||
|
||||
$isSlackEnabled = data_get($notifiable, 'slack_enabled');
|
||||
if ($isDiscordEnabled) {
|
||||
$channels[] = DiscordChannel::class;
|
||||
}
|
||||
@@ -33,6 +35,9 @@ class ForceDisabled extends CustomEmailNotification
|
||||
if ($isTelegramEnabled) {
|
||||
$channels[] = TelegramChannel::class;
|
||||
}
|
||||
if ($isSlackEnabled) {
|
||||
$channels[] = SlackChannel::class;
|
||||
}
|
||||
|
||||
return $channels;
|
||||
}
|
||||
@@ -67,4 +72,19 @@ class ForceDisabled extends CustomEmailNotification
|
||||
'message' => "Coolify: Server ({$this->server->name}) disabled because it is not paid!\n All automations and integrations are stopped.\nPlease update your subscription to enable the server again [here](https://app.coolify.io/subscriptions).",
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
public function toSlack(): SlackMessage
|
||||
{
|
||||
$title = "Server disabled";
|
||||
$description = "Server ({$this->server->name}) disabled because it is not paid!\n";
|
||||
$description .= "All automations and integrations are stopped.\n\n";
|
||||
$description .= "Please update your subscription to enable the server again: https://app.coolify.io/subscriptions";
|
||||
|
||||
return new SlackMessage(
|
||||
title: $title,
|
||||
description: $description,
|
||||
color: SlackMessage::errorColor()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,8 @@ use App\Models\Server;
|
||||
use App\Notifications\Channels\DiscordChannel;
|
||||
use App\Notifications\Channels\EmailChannel;
|
||||
use App\Notifications\Channels\TelegramChannel;
|
||||
use App\Notifications\Channels\SlackChannel;
|
||||
use App\Notifications\Dto\SlackMessage;
|
||||
use App\Notifications\CustomEmailNotification;
|
||||
use App\Notifications\Dto\DiscordMessage;
|
||||
use Illuminate\Notifications\Messages\MailMessage;
|
||||
@@ -23,7 +25,7 @@ class ForceEnabled extends CustomEmailNotification
|
||||
$isEmailEnabled = isEmailEnabled($notifiable);
|
||||
$isDiscordEnabled = data_get($notifiable, 'discord_enabled');
|
||||
$isTelegramEnabled = data_get($notifiable, 'telegram_enabled');
|
||||
|
||||
$isSlackEnabled = data_get($notifiable, 'slack_enabled');
|
||||
if ($isDiscordEnabled) {
|
||||
$channels[] = DiscordChannel::class;
|
||||
}
|
||||
@@ -33,6 +35,9 @@ class ForceEnabled extends CustomEmailNotification
|
||||
if ($isTelegramEnabled) {
|
||||
$channels[] = TelegramChannel::class;
|
||||
}
|
||||
if ($isSlackEnabled) {
|
||||
$channels[] = SlackChannel::class;
|
||||
}
|
||||
|
||||
return $channels;
|
||||
}
|
||||
@@ -63,4 +68,15 @@ class ForceEnabled extends CustomEmailNotification
|
||||
'message' => "Coolify: Server ({$this->server->name}) enabled again!",
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
public function toSlack(): SlackMessage
|
||||
{
|
||||
return new SlackMessage(
|
||||
title: 'Server enabled',
|
||||
description: "Server '{$this->server->name}' enabled again!",
|
||||
color: SlackMessage::successColor()
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ namespace App\Notifications\Server;
|
||||
use App\Models\Server;
|
||||
use App\Notifications\CustomEmailNotification;
|
||||
use App\Notifications\Dto\DiscordMessage;
|
||||
use App\Notifications\Dto\SlackMessage;
|
||||
use Illuminate\Notifications\Messages\MailMessage;
|
||||
|
||||
class HighDiskUsage extends CustomEmailNotification
|
||||
@@ -44,7 +45,7 @@ class HighDiskUsage extends CustomEmailNotification
|
||||
$message->addField('Disk usage', "{$this->disk_usage}%", true);
|
||||
$message->addField('Threshold', "{$this->server_disk_usage_notification_threshold}%", true);
|
||||
$message->addField('What to do?', '[Link](https://coolify.io/docs/knowledge-base/server/automated-cleanup)', true);
|
||||
$message->addField('Change Settings', '[Threshold]('.base_url().'/server/'.$this->server->uuid.'#advanced) | [Notification]('.base_url().'/notifications/discord)');
|
||||
$message->addField('Change Settings', '[Threshold](' . base_url() . '/server/' . $this->server->uuid . '#advanced) | [Notification](' . base_url() . '/notifications/discord)');
|
||||
|
||||
return $message;
|
||||
}
|
||||
@@ -55,4 +56,22 @@ class HighDiskUsage extends CustomEmailNotification
|
||||
'message' => "Coolify: Server '{$this->server->name}' high disk usage detected!\nDisk usage: {$this->disk_usage}%. Threshold: {$this->server_disk_usage_notification_threshold}%.\nPlease cleanup your disk to prevent data-loss.\nHere are some tips: https://coolify.io/docs/knowledge-base/server/automated-cleanup.",
|
||||
];
|
||||
}
|
||||
|
||||
public function toSlack(): SlackMessage
|
||||
{
|
||||
$description = "Server '{$this->server->name}' high disk usage detected!\n";
|
||||
$description .= "Disk usage: {$this->disk_usage}%\n";
|
||||
$description .= "Threshold: {$this->server_disk_usage_notification_threshold}%\n\n";
|
||||
$description .= "Please cleanup your disk to prevent data-loss.\n";
|
||||
$description .= "Tips for cleanup: https://coolify.io/docs/knowledge-base/server/automated-cleanup\n";
|
||||
$description .= "Change settings:\n";
|
||||
$description .= "- Threshold: " . base_url() . "/server/" . $this->server->uuid . "#advanced\n";
|
||||
$description .= "- Notifications: " . base_url() . "/notifications/discord";
|
||||
|
||||
return new SlackMessage(
|
||||
title: 'High disk usage detected',
|
||||
description: $description,
|
||||
color: SlackMessage::errorColor()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,8 @@ use App\Models\Server;
|
||||
use App\Notifications\Channels\DiscordChannel;
|
||||
use App\Notifications\Channels\EmailChannel;
|
||||
use App\Notifications\Channels\TelegramChannel;
|
||||
use App\Notifications\Channels\SlackChannel;
|
||||
use App\Notifications\Dto\SlackMessage;
|
||||
use App\Notifications\CustomEmailNotification;
|
||||
use App\Notifications\Dto\DiscordMessage;
|
||||
use Illuminate\Notifications\Messages\MailMessage;
|
||||
@@ -18,7 +20,7 @@ class Reachable extends CustomEmailNotification
|
||||
{
|
||||
$this->onQueue('high');
|
||||
$this->isRateLimited = isEmailRateLimited(
|
||||
limiterKey: 'server-reachable:'.$this->server->id,
|
||||
limiterKey: 'server-reachable:' . $this->server->id,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -32,7 +34,7 @@ class Reachable extends CustomEmailNotification
|
||||
$isEmailEnabled = isEmailEnabled($notifiable);
|
||||
$isDiscordEnabled = data_get($notifiable, 'discord_enabled');
|
||||
$isTelegramEnabled = data_get($notifiable, 'telegram_enabled');
|
||||
|
||||
$isSlackEnabled = data_get($notifiable, 'slack_enabled');
|
||||
if ($isDiscordEnabled) {
|
||||
$channels[] = DiscordChannel::class;
|
||||
}
|
||||
@@ -42,6 +44,9 @@ class Reachable extends CustomEmailNotification
|
||||
if ($isTelegramEnabled) {
|
||||
$channels[] = TelegramChannel::class;
|
||||
}
|
||||
if ($isSlackEnabled) {
|
||||
$channels[] = SlackChannel::class;
|
||||
}
|
||||
|
||||
return $channels;
|
||||
}
|
||||
@@ -72,4 +77,16 @@ class Reachable extends CustomEmailNotification
|
||||
'message' => "Coolify: Server '{$this->server->name}' revived. All automations & integrations are turned on again!",
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public function toSlack(): SlackMessage
|
||||
{
|
||||
return new SlackMessage(
|
||||
title: "Server revived",
|
||||
description: "Server '{$this->server->name}' revived.\nAll automations & integrations are turned on again!",
|
||||
color: SlackMessage::successColor()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,8 @@ use App\Models\Server;
|
||||
use App\Notifications\Channels\DiscordChannel;
|
||||
use App\Notifications\Channels\EmailChannel;
|
||||
use App\Notifications\Channels\TelegramChannel;
|
||||
use App\Notifications\Channels\SlackChannel;
|
||||
use App\Notifications\Dto\SlackMessage;
|
||||
use App\Notifications\CustomEmailNotification;
|
||||
use App\Notifications\Dto\DiscordMessage;
|
||||
use Illuminate\Notifications\Messages\MailMessage;
|
||||
@@ -18,7 +20,7 @@ class Unreachable extends CustomEmailNotification
|
||||
{
|
||||
$this->onQueue('high');
|
||||
$this->isRateLimited = isEmailRateLimited(
|
||||
limiterKey: 'server-unreachable:'.$this->server->id,
|
||||
limiterKey: 'server-unreachable:' . $this->server->id,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -32,6 +34,7 @@ class Unreachable extends CustomEmailNotification
|
||||
$isEmailEnabled = isEmailEnabled($notifiable);
|
||||
$isDiscordEnabled = data_get($notifiable, 'discord_enabled');
|
||||
$isTelegramEnabled = data_get($notifiable, 'telegram_enabled');
|
||||
$isSlackEnabled = data_get($notifiable, 'slack_enabled');
|
||||
|
||||
if ($isDiscordEnabled) {
|
||||
$channels[] = DiscordChannel::class;
|
||||
@@ -42,6 +45,9 @@ class Unreachable extends CustomEmailNotification
|
||||
if ($isTelegramEnabled) {
|
||||
$channels[] = TelegramChannel::class;
|
||||
}
|
||||
if ($isSlackEnabled) {
|
||||
$channels[] = SlackChannel::class;
|
||||
}
|
||||
|
||||
return $channels;
|
||||
}
|
||||
@@ -76,4 +82,18 @@ class Unreachable extends CustomEmailNotification
|
||||
'message' => "Coolify: Your server '{$this->server->name}' is unreachable. All automations & integrations are turned off! Please check your server! IMPORTANT: We automatically try to revive your server and turn on all automations & integrations.",
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
public function toSlack(): SlackMessage
|
||||
{
|
||||
$description = "Your server '{$this->server->name}' is unreachable.\n";
|
||||
$description .= "All automations & integrations are turned off!\n\n";
|
||||
$description .= "*IMPORTANT:* We automatically try to revive your server and turn on all automations & integrations.";
|
||||
|
||||
return new SlackMessage(
|
||||
title: 'Server unreachable',
|
||||
description: $description,
|
||||
color: SlackMessage::errorColor()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
namespace App\Notifications;
|
||||
|
||||
use App\Notifications\Dto\DiscordMessage;
|
||||
use App\Notifications\Dto\SlackMessage;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Notifications\Messages\MailMessage;
|
||||
@@ -67,4 +68,12 @@ class Test extends Notification implements ShouldQueue
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
public function toSlack(): SlackMessage
|
||||
{
|
||||
return new SlackMessage(
|
||||
title: 'Test Slack Notification',
|
||||
description: 'This is a test Slack notification from Coolify.'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -173,13 +173,12 @@ function generate_default_proxy_configuration(Server $server)
|
||||
],
|
||||
'volumes' => [
|
||||
'/var/run/docker.sock:/var/run/docker.sock:ro',
|
||||
"{$proxy_path}:/traefik",
|
||||
|
||||
],
|
||||
'command' => [
|
||||
'--ping=true',
|
||||
'--ping.entrypoint=http',
|
||||
'--api.dashboard=true',
|
||||
'--api.insecure=false',
|
||||
'--entrypoints.http.address=:80',
|
||||
'--entrypoints.https.address=:443',
|
||||
'--entrypoints.http.http.encodequerysemicolons=true',
|
||||
@@ -187,21 +186,26 @@ function generate_default_proxy_configuration(Server $server)
|
||||
'--entrypoints.https.http.encodequerysemicolons=true',
|
||||
'--entryPoints.https.http2.maxConcurrentStreams=50',
|
||||
'--entrypoints.https.http3',
|
||||
'--providers.docker.exposedbydefault=false',
|
||||
'--providers.file.directory=/traefik/dynamic/',
|
||||
'--providers.docker.exposedbydefault=false',
|
||||
'--providers.file.watch=true',
|
||||
'--certificatesresolvers.letsencrypt.acme.httpchallenge=true',
|
||||
'--certificatesresolvers.letsencrypt.acme.storage=/traefik/acme.json',
|
||||
'--certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=http',
|
||||
'--certificatesresolvers.letsencrypt.acme.storage=/traefik/acme.json',
|
||||
],
|
||||
'labels' => $labels,
|
||||
],
|
||||
],
|
||||
];
|
||||
if (isDev()) {
|
||||
// $config['services']['traefik']['command'][] = "--log.level=debug";
|
||||
$config['services']['traefik']['command'][] = '--api.insecure=true';
|
||||
$config['services']['traefik']['command'][] = '--log.level=debug';
|
||||
$config['services']['traefik']['command'][] = '--accesslog.filepath=/traefik/access.log';
|
||||
$config['services']['traefik']['command'][] = '--accesslog.bufferingsize=100';
|
||||
$config['services']['traefik']['volumes'][] = '/var/lib/docker/volumes/coolify_dev_coolify_data/_data/proxy/:/traefik';
|
||||
} else {
|
||||
$config['services']['traefik']['command'][] = '--api.insecure=false';
|
||||
$config['services']['traefik']['volumes'][] = "{$proxy_path}:/traefik";
|
||||
}
|
||||
if ($server->isSwarm()) {
|
||||
data_forget($config, 'services.traefik.container_name');
|
||||
|
||||
@@ -27,6 +27,7 @@ use App\Models\Team;
|
||||
use App\Models\User;
|
||||
use App\Notifications\Channels\DiscordChannel;
|
||||
use App\Notifications\Channels\EmailChannel;
|
||||
use App\Notifications\Channels\SlackChannel;
|
||||
use App\Notifications\Channels\TelegramChannel;
|
||||
use App\Notifications\Internal\GeneralNotification;
|
||||
use Carbon\CarbonImmutable;
|
||||
@@ -67,27 +68,27 @@ function base_configuration_dir(): string
|
||||
}
|
||||
function application_configuration_dir(): string
|
||||
{
|
||||
return base_configuration_dir().'/applications';
|
||||
return base_configuration_dir() . '/applications';
|
||||
}
|
||||
function service_configuration_dir(): string
|
||||
{
|
||||
return base_configuration_dir().'/services';
|
||||
return base_configuration_dir() . '/services';
|
||||
}
|
||||
function database_configuration_dir(): string
|
||||
{
|
||||
return base_configuration_dir().'/databases';
|
||||
return base_configuration_dir() . '/databases';
|
||||
}
|
||||
function database_proxy_dir($uuid): string
|
||||
{
|
||||
return base_configuration_dir()."/databases/$uuid/proxy";
|
||||
return base_configuration_dir() . "/databases/$uuid/proxy";
|
||||
}
|
||||
function backup_dir(): string
|
||||
{
|
||||
return base_configuration_dir().'/backups';
|
||||
return base_configuration_dir() . '/backups';
|
||||
}
|
||||
function metrics_dir(): string
|
||||
{
|
||||
return base_configuration_dir().'/metrics';
|
||||
return base_configuration_dir() . '/metrics';
|
||||
}
|
||||
|
||||
function sanitize_string(?string $input = null): ?string
|
||||
@@ -138,15 +139,15 @@ function showBoarding(): bool
|
||||
}
|
||||
function refreshSession(?Team $team = null): void
|
||||
{
|
||||
if (! $team) {
|
||||
if (!$team) {
|
||||
if (Auth::user()->currentTeam()) {
|
||||
$team = Team::find(Auth::user()->currentTeam()->id);
|
||||
} else {
|
||||
$team = User::find(Auth::id())->teams->first();
|
||||
}
|
||||
}
|
||||
Cache::forget('team:'.Auth::id());
|
||||
Cache::remember('team:'.Auth::id(), 3600, function () use ($team) {
|
||||
Cache::forget('team:' . Auth::id());
|
||||
Cache::remember('team:' . Auth::id(), 3600, function () use ($team) {
|
||||
return $team;
|
||||
});
|
||||
session(['currentTeam' => $team]);
|
||||
@@ -178,7 +179,7 @@ function handleError(?Throwable $error = null, ?Livewire\Component $livewire = n
|
||||
$message = null;
|
||||
}
|
||||
if ($customErrorMessage) {
|
||||
$message = $customErrorMessage.' '.$message;
|
||||
$message = $customErrorMessage . ' ' . $message;
|
||||
}
|
||||
|
||||
if (isset($livewire)) {
|
||||
@@ -251,7 +252,7 @@ function generateSSHKey(string $type = 'rsa')
|
||||
function formatPrivateKey(string $privateKey)
|
||||
{
|
||||
$privateKey = trim($privateKey);
|
||||
if (! str_ends_with($privateKey, "\n")) {
|
||||
if (!str_ends_with($privateKey, "\n")) {
|
||||
$privateKey .= "\n";
|
||||
}
|
||||
|
||||
@@ -273,7 +274,7 @@ function is_transactional_emails_active(): bool
|
||||
|
||||
function set_transanctional_email_settings(?InstanceSettings $settings = null): ?string
|
||||
{
|
||||
if (! $settings) {
|
||||
if (!$settings) {
|
||||
$settings = instanceSettings();
|
||||
}
|
||||
config()->set('mail.from.address', data_get($settings, 'smtp_from_address'));
|
||||
@@ -373,7 +374,7 @@ function isSubscribed()
|
||||
|
||||
function isProduction(): bool
|
||||
{
|
||||
return ! isDev();
|
||||
return !isDev();
|
||||
}
|
||||
function isDev(): bool
|
||||
{
|
||||
@@ -426,14 +427,14 @@ function send_user_an_email(MailMessage $mail, string $email, ?string $cc = null
|
||||
{
|
||||
$settings = instanceSettings();
|
||||
$type = set_transanctional_email_settings($settings);
|
||||
if (! $type) {
|
||||
if (!$type) {
|
||||
throw new Exception('No email settings found.');
|
||||
}
|
||||
if ($cc) {
|
||||
Mail::send(
|
||||
[],
|
||||
[],
|
||||
fn (Message $message) => $message
|
||||
fn(Message $message) => $message
|
||||
->to($email)
|
||||
->replyTo($email)
|
||||
->cc($cc)
|
||||
@@ -444,7 +445,7 @@ function send_user_an_email(MailMessage $mail, string $email, ?string $cc = null
|
||||
Mail::send(
|
||||
[],
|
||||
[],
|
||||
fn (Message $message) => $message
|
||||
fn(Message $message) => $message
|
||||
->to($email)
|
||||
->subject($mail->subject)
|
||||
->html((string) $mail->render())
|
||||
@@ -469,11 +470,13 @@ function setNotificationChannels($notifiable, $event)
|
||||
{
|
||||
$channels = [];
|
||||
$isEmailEnabled = isEmailEnabled($notifiable);
|
||||
$isSlackEnabled = data_get($notifiable, 'slack_enabled');
|
||||
$isDiscordEnabled = data_get($notifiable, 'discord_enabled');
|
||||
$isTelegramEnabled = data_get($notifiable, 'telegram_enabled');
|
||||
$isSubscribedToEmailEvent = data_get($notifiable, "smtp_notifications_$event");
|
||||
$isSubscribedToDiscordEvent = data_get($notifiable, "discord_notifications_$event");
|
||||
$isSubscribedToTelegramEvent = data_get($notifiable, "telegram_notifications_$event");
|
||||
$isSubscribedToSlackEvent = data_get($notifiable, "slack_notifications_$event");
|
||||
|
||||
if ($isDiscordEnabled && $isSubscribedToDiscordEvent) {
|
||||
$channels[] = DiscordChannel::class;
|
||||
@@ -484,6 +487,9 @@ function setNotificationChannels($notifiable, $event)
|
||||
if ($isTelegramEnabled && $isSubscribedToTelegramEvent) {
|
||||
$channels[] = TelegramChannel::class;
|
||||
}
|
||||
if ($isSlackEnabled && $isSubscribedToSlackEvent) {
|
||||
$channels[] = SlackChannel::class;
|
||||
}
|
||||
|
||||
return $channels;
|
||||
}
|
||||
@@ -588,7 +594,7 @@ function getResourceByUuid(string $uuid, ?int $teamId = null)
|
||||
return null;
|
||||
}
|
||||
$resource = queryResourcesByUuid($uuid);
|
||||
if (! is_null($resource) && $resource->environment->project->team_id === $teamId) {
|
||||
if (!is_null($resource) && $resource->environment->project->team_id === $teamId) {
|
||||
return $resource;
|
||||
}
|
||||
|
||||
@@ -680,29 +686,29 @@ function queryResourcesByUuid(string $uuid)
|
||||
function generateTagDeployWebhook($tag_name)
|
||||
{
|
||||
$baseUrl = base_url();
|
||||
$api = Url::fromString($baseUrl).'/api/v1';
|
||||
$api = Url::fromString($baseUrl) . '/api/v1';
|
||||
$endpoint = "/deploy?tag=$tag_name";
|
||||
|
||||
return $api.$endpoint;
|
||||
return $api . $endpoint;
|
||||
}
|
||||
function generateDeployWebhook($resource)
|
||||
{
|
||||
$baseUrl = base_url();
|
||||
$api = Url::fromString($baseUrl).'/api/v1';
|
||||
$api = Url::fromString($baseUrl) . '/api/v1';
|
||||
$endpoint = '/deploy';
|
||||
$uuid = data_get($resource, 'uuid');
|
||||
|
||||
return $api.$endpoint."?uuid=$uuid&force=false";
|
||||
return $api . $endpoint . "?uuid=$uuid&force=false";
|
||||
}
|
||||
function generateGitManualWebhook($resource, $type)
|
||||
{
|
||||
if ($resource->source_id !== 0 && ! is_null($resource->source_id)) {
|
||||
if ($resource->source_id !== 0 && !is_null($resource->source_id)) {
|
||||
return null;
|
||||
}
|
||||
if ($resource->getMorphClass() === \App\Models\Application::class) {
|
||||
$baseUrl = base_url();
|
||||
|
||||
return Url::fromString($baseUrl)."/webhooks/source/$type/events/manual";
|
||||
return Url::fromString($baseUrl) . "/webhooks/source/$type/events/manual";
|
||||
}
|
||||
|
||||
return null;
|
||||
@@ -729,7 +735,7 @@ function getTopLevelNetworks(Service|Application $resource)
|
||||
$hasHostNetworkMode = data_get($service, 'network_mode') === 'host' ? true : false;
|
||||
|
||||
// Only add 'networks' key if 'network_mode' is not 'host'
|
||||
if (! $hasHostNetworkMode) {
|
||||
if (!$hasHostNetworkMode) {
|
||||
// Collect/create/update networks
|
||||
if ($serviceNetworks->count() > 0) {
|
||||
foreach ($serviceNetworks as $networkName => $networkDetails) {
|
||||
@@ -743,7 +749,7 @@ function getTopLevelNetworks(Service|Application $resource)
|
||||
$networkExists = $topLevelNetworks->contains(function ($value, $key) use ($networkName) {
|
||||
return $value == $networkName || $key == $networkName;
|
||||
});
|
||||
if (! $networkExists) {
|
||||
if (!$networkExists) {
|
||||
if (is_string($networkDetails) || is_int($networkDetails)) {
|
||||
$topLevelNetworks->put($networkDetails, null);
|
||||
}
|
||||
@@ -754,7 +760,7 @@ function getTopLevelNetworks(Service|Application $resource)
|
||||
$definedNetworkExists = $topLevelNetworks->contains(function ($value, $_) use ($definedNetwork) {
|
||||
return $value == $definedNetwork;
|
||||
});
|
||||
if (! $definedNetworkExists) {
|
||||
if (!$definedNetworkExists) {
|
||||
foreach ($definedNetwork as $network) {
|
||||
$topLevelNetworks->put($network, [
|
||||
'name' => $network,
|
||||
@@ -795,7 +801,7 @@ function getTopLevelNetworks(Service|Application $resource)
|
||||
$networkExists = $topLevelNetworks->contains(function ($value, $key) use ($networkName) {
|
||||
return $value == $networkName || $key == $networkName;
|
||||
});
|
||||
if (! $networkExists) {
|
||||
if (!$networkExists) {
|
||||
if (is_string($networkDetails) || is_int($networkDetails)) {
|
||||
$topLevelNetworks->put($networkDetails, null);
|
||||
}
|
||||
@@ -805,7 +811,7 @@ function getTopLevelNetworks(Service|Application $resource)
|
||||
$definedNetworkExists = $topLevelNetworks->contains(function ($value, $_) use ($definedNetwork) {
|
||||
return $value == $definedNetwork;
|
||||
});
|
||||
if (! $definedNetworkExists) {
|
||||
if (!$definedNetworkExists) {
|
||||
foreach ($definedNetwork as $network) {
|
||||
$topLevelNetworks->put($network, [
|
||||
'name' => $network,
|
||||
@@ -941,7 +947,7 @@ function generateEnvValue(string $command, Service|Application|null $service = n
|
||||
case 'PASSWORD_64':
|
||||
$generatedValue = Str::password(length: 64, symbols: false);
|
||||
break;
|
||||
// This is not base64, it's just a random string
|
||||
// This is not base64, it's just a random string
|
||||
case 'BASE64_64':
|
||||
$generatedValue = Str::random(64);
|
||||
break;
|
||||
@@ -952,7 +958,7 @@ function generateEnvValue(string $command, Service|Application|null $service = n
|
||||
case 'BASE64_32':
|
||||
$generatedValue = Str::random(32);
|
||||
break;
|
||||
// This is base64,
|
||||
// This is base64,
|
||||
case 'REALBASE64_64':
|
||||
$generatedValue = base64_encode(Str::random(64));
|
||||
break;
|
||||
@@ -1052,7 +1058,7 @@ function validate_dns_entry(string $fqdn, Server $server)
|
||||
}
|
||||
$settings = instanceSettings();
|
||||
$is_dns_validation_enabled = data_get($settings, 'is_dns_validation_enabled');
|
||||
if (! $is_dns_validation_enabled) {
|
||||
if (!$is_dns_validation_enabled) {
|
||||
return true;
|
||||
}
|
||||
$dns_servers = data_get($settings, 'custom_dns_servers');
|
||||
@@ -1070,7 +1076,7 @@ function validate_dns_entry(string $fqdn, Server $server)
|
||||
$query = new DNSQuery($dns_server);
|
||||
$results = $query->query($host, $type);
|
||||
if ($results === false || $query->hasError()) {
|
||||
ray('Error: '.$query->getLasterror());
|
||||
ray('Error: ' . $query->getLasterror());
|
||||
} else {
|
||||
foreach ($results as $result) {
|
||||
if ($result->getType() == $type) {
|
||||
@@ -1080,7 +1086,7 @@ function validate_dns_entry(string $fqdn, Server $server)
|
||||
break;
|
||||
}
|
||||
if ($result->getData() === $ip) {
|
||||
ray($host.' has IP address '.$result->getData());
|
||||
ray($host . ' has IP address ' . $result->getData());
|
||||
ray($result->getString());
|
||||
$found_matching_ip = true;
|
||||
break;
|
||||
@@ -1128,15 +1134,15 @@ function checkIfDomainIsAlreadyUsed(Collection|array $domains, ?string $teamId =
|
||||
$applications = Application::ownedByCurrentTeamAPI($teamId)->get(['fqdn', 'uuid']);
|
||||
$serviceApplications = ServiceApplication::ownedByCurrentTeamAPI($teamId)->get(['fqdn', 'uuid']);
|
||||
if ($uuid) {
|
||||
$applications = $applications->filter(fn ($app) => $app->uuid !== $uuid);
|
||||
$serviceApplications = $serviceApplications->filter(fn ($app) => $app->uuid !== $uuid);
|
||||
$applications = $applications->filter(fn($app) => $app->uuid !== $uuid);
|
||||
$serviceApplications = $serviceApplications->filter(fn($app) => $app->uuid !== $uuid);
|
||||
}
|
||||
$domainFound = false;
|
||||
foreach ($applications as $app) {
|
||||
if (is_null($app->fqdn)) {
|
||||
continue;
|
||||
}
|
||||
$list_of_domains = collect(explode(',', $app->fqdn))->filter(fn ($fqdn) => $fqdn !== '');
|
||||
$list_of_domains = collect(explode(',', $app->fqdn))->filter(fn($fqdn) => $fqdn !== '');
|
||||
foreach ($list_of_domains as $domain) {
|
||||
if (str($domain)->endsWith('/')) {
|
||||
$domain = str($domain)->beforeLast('/');
|
||||
@@ -1155,7 +1161,7 @@ function checkIfDomainIsAlreadyUsed(Collection|array $domains, ?string $teamId =
|
||||
if (str($app->fqdn)->isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
$list_of_domains = collect(explode(',', $app->fqdn))->filter(fn ($fqdn) => $fqdn !== '');
|
||||
$list_of_domains = collect(explode(',', $app->fqdn))->filter(fn($fqdn) => $fqdn !== '');
|
||||
foreach ($list_of_domains as $domain) {
|
||||
if (str($domain)->endsWith('/')) {
|
||||
$domain = str($domain)->beforeLast('/');
|
||||
@@ -1205,7 +1211,7 @@ function check_domain_usage(ServiceApplication|Application|null $resource = null
|
||||
});
|
||||
$apps = Application::all();
|
||||
foreach ($apps as $app) {
|
||||
$list_of_domains = collect(explode(',', $app->fqdn))->filter(fn ($fqdn) => $fqdn !== '');
|
||||
$list_of_domains = collect(explode(',', $app->fqdn))->filter(fn($fqdn) => $fqdn !== '');
|
||||
foreach ($list_of_domains as $domain) {
|
||||
if (str($domain)->endsWith('/')) {
|
||||
$domain = str($domain)->beforeLast('/');
|
||||
@@ -1224,7 +1230,7 @@ function check_domain_usage(ServiceApplication|Application|null $resource = null
|
||||
}
|
||||
$apps = ServiceApplication::all();
|
||||
foreach ($apps as $app) {
|
||||
$list_of_domains = collect(explode(',', $app->fqdn))->filter(fn ($fqdn) => $fqdn !== '');
|
||||
$list_of_domains = collect(explode(',', $app->fqdn))->filter(fn($fqdn) => $fqdn !== '');
|
||||
foreach ($list_of_domains as $domain) {
|
||||
if (str($domain)->endsWith('/')) {
|
||||
$domain = str($domain)->beforeLast('/');
|
||||
@@ -1260,7 +1266,7 @@ function parseCommandsByLineForSudo(Collection $commands, Server $server): array
|
||||
{
|
||||
$commands = $commands->map(function ($line) {
|
||||
if (
|
||||
! str(trim($line))->startsWith([
|
||||
!str(trim($line))->startsWith([
|
||||
'cd',
|
||||
'command',
|
||||
'echo',
|
||||
@@ -1281,7 +1287,7 @@ function parseCommandsByLineForSudo(Collection $commands, Server $server): array
|
||||
|
||||
$commands = $commands->map(function ($line) use ($server) {
|
||||
if (Str::startsWith($line, 'sudo mkdir -p')) {
|
||||
return "$line && sudo chown -R $server->user:$server->user ".Str::after($line, 'sudo mkdir -p').' && sudo chmod -R o-rwx '.Str::after($line, 'sudo mkdir -p');
|
||||
return "$line && sudo chown -R $server->user:$server->user " . Str::after($line, 'sudo mkdir -p') . ' && sudo chmod -R o-rwx ' . Str::after($line, 'sudo mkdir -p');
|
||||
}
|
||||
|
||||
return $line;
|
||||
@@ -1309,11 +1315,11 @@ function parseCommandsByLineForSudo(Collection $commands, Server $server): array
|
||||
}
|
||||
function parseLineForSudo(string $command, Server $server): string
|
||||
{
|
||||
if (! str($command)->startSwith('cd') && ! str($command)->startSwith('command')) {
|
||||
if (!str($command)->startSwith('cd') && !str($command)->startSwith('command')) {
|
||||
$command = "sudo $command";
|
||||
}
|
||||
if (Str::startsWith($command, 'sudo mkdir -p')) {
|
||||
$command = "$command && sudo chown -R $server->user:$server->user ".Str::after($command, 'sudo mkdir -p').' && sudo chmod -R o-rwx '.Str::after($command, 'sudo mkdir -p');
|
||||
$command = "$command && sudo chown -R $server->user:$server->user " . Str::after($command, 'sudo mkdir -p') . ' && sudo chmod -R o-rwx ' . Str::after($command, 'sudo mkdir -p');
|
||||
}
|
||||
if (str($command)->contains('$(') || str($command)->contains('`')) {
|
||||
$command = str($command)->replace('$(', '$(sudo ')->replace('`', '`sudo ')->value();
|
||||
@@ -1435,7 +1441,7 @@ function parseServiceVolumes($serviceVolumes, $resource, $topLevelVolumes, $pull
|
||||
$isDirectory = data_get($foundConfig, 'is_directory');
|
||||
} else {
|
||||
$isDirectory = (bool) data_get($volume, 'isDirectory', null) || (bool) data_get($volume, 'is_directory', null);
|
||||
if ((is_null($isDirectory) || ! $isDirectory) && is_null($content)) {
|
||||
if ((is_null($isDirectory) || !$isDirectory) && is_null($content)) {
|
||||
// if isDirectory is not set (or false) & content is also not set, we assume it is a directory
|
||||
ray('setting isDirectory to true');
|
||||
$isDirectory = true;
|
||||
@@ -1450,9 +1456,9 @@ function parseServiceVolumes($serviceVolumes, $resource, $topLevelVolumes, $pull
|
||||
return $volume;
|
||||
}
|
||||
if (get_class($resource) === \App\Models\Application::class) {
|
||||
$dir = base_configuration_dir().'/applications/'.$resource->uuid;
|
||||
$dir = base_configuration_dir() . '/applications/' . $resource->uuid;
|
||||
} else {
|
||||
$dir = base_configuration_dir().'/services/'.$resource->service->uuid;
|
||||
$dir = base_configuration_dir() . '/services/' . $resource->service->uuid;
|
||||
}
|
||||
|
||||
if ($source->startsWith('.')) {
|
||||
@@ -1462,9 +1468,9 @@ function parseServiceVolumes($serviceVolumes, $resource, $topLevelVolumes, $pull
|
||||
$source = $source->replaceFirst('~', $dir);
|
||||
}
|
||||
if ($pull_request_id !== 0) {
|
||||
$source = $source."-pr-$pull_request_id";
|
||||
$source = $source . "-pr-$pull_request_id";
|
||||
}
|
||||
if (! $resource?->settings?->is_preserve_repository_enabled || $foundConfig?->is_based_on_git) {
|
||||
if (!$resource?->settings?->is_preserve_repository_enabled || $foundConfig?->is_based_on_git) {
|
||||
LocalFileVolume::updateOrCreate(
|
||||
[
|
||||
'mount_path' => $target,
|
||||
@@ -1593,7 +1599,7 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
|
||||
if ($serviceLabels->count() > 0) {
|
||||
$removedLabels = collect([]);
|
||||
$serviceLabels = $serviceLabels->filter(function ($serviceLabel, $serviceLabelName) use ($removedLabels) {
|
||||
if (! str($serviceLabel)->contains('=')) {
|
||||
if (!str($serviceLabel)->contains('=')) {
|
||||
$removedLabels->put($serviceLabelName, $serviceLabel);
|
||||
|
||||
return false;
|
||||
@@ -1675,7 +1681,7 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
|
||||
$networkExists = $topLevelNetworks->contains(function ($value, $key) use ($networkName) {
|
||||
return $value == $networkName || $key == $networkName;
|
||||
});
|
||||
if (! $networkExists) {
|
||||
if (!$networkExists) {
|
||||
if (is_string($networkDetails) || is_int($networkDetails)) {
|
||||
$topLevelNetworks->put($networkDetails, null);
|
||||
}
|
||||
@@ -1701,12 +1707,12 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
|
||||
$savedService->ports = $collectedPorts->implode(',');
|
||||
$savedService->save();
|
||||
|
||||
if (! $hasHostNetworkMode) {
|
||||
if (!$hasHostNetworkMode) {
|
||||
// Add Coolify specific networks
|
||||
$definedNetworkExists = $topLevelNetworks->contains(function ($value, $_) use ($definedNetwork) {
|
||||
return $value == $definedNetwork;
|
||||
});
|
||||
if (! $definedNetworkExists) {
|
||||
if (!$definedNetworkExists) {
|
||||
foreach ($definedNetwork as $network) {
|
||||
$topLevelNetworks->put($network, [
|
||||
'name' => $network,
|
||||
@@ -1918,9 +1924,9 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
|
||||
$fqdn = "$fqdn$path";
|
||||
}
|
||||
|
||||
if (! $isDatabase) {
|
||||
if (!$isDatabase) {
|
||||
if ($savedService->fqdn) {
|
||||
data_set($savedService, 'fqdn', $savedService->fqdn.','.$fqdn);
|
||||
data_set($savedService, 'fqdn', $savedService->fqdn . ',' . $fqdn);
|
||||
} else {
|
||||
data_set($savedService, 'fqdn', $fqdn);
|
||||
}
|
||||
@@ -1935,7 +1941,7 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
|
||||
]);
|
||||
}
|
||||
// Caddy needs exact port in some cases.
|
||||
if ($predefinedPort && ! $key->endsWith("_{$predefinedPort}")) {
|
||||
if ($predefinedPort && !$key->endsWith("_{$predefinedPort}")) {
|
||||
$fqdns_exploded = str($savedService->fqdn)->explode(',');
|
||||
if ($fqdns_exploded->count() > 1) {
|
||||
continue;
|
||||
@@ -1975,12 +1981,12 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
|
||||
'service_id' => $resource->id,
|
||||
])->first();
|
||||
['command' => $command, 'forService' => $forService, 'generatedValue' => $generatedValue, 'port' => $port] = parseEnvVariable($value);
|
||||
if (! is_null($command)) {
|
||||
if (!is_null($command)) {
|
||||
if ($command?->value() === 'FQDN' || $command?->value() === 'URL') {
|
||||
if (Str::lower($forService) === $serviceName) {
|
||||
$fqdn = generateFqdn($resource->server, $containerName);
|
||||
} else {
|
||||
$fqdn = generateFqdn($resource->server, Str::lower($forService).'-'.$resource->uuid);
|
||||
$fqdn = generateFqdn($resource->server, Str::lower($forService) . '-' . $resource->uuid);
|
||||
}
|
||||
if ($port) {
|
||||
$fqdn = "$fqdn:$port";
|
||||
@@ -2010,13 +2016,13 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
|
||||
'is_preview' => false,
|
||||
]);
|
||||
}
|
||||
if (! $isDatabase) {
|
||||
if ($command->value() === 'FQDN' && is_null($savedService->fqdn) && ! $foundEnv) {
|
||||
if (!$isDatabase) {
|
||||
if ($command->value() === 'FQDN' && is_null($savedService->fqdn) && !$foundEnv) {
|
||||
$savedService->fqdn = $fqdn;
|
||||
$savedService->save();
|
||||
}
|
||||
// Caddy needs exact port in some cases.
|
||||
if ($predefinedPort && ! $key->endsWith("_{$predefinedPort}") && $command?->value() === 'FQDN' && $resource->server->proxyType() === 'CADDY') {
|
||||
if ($predefinedPort && !$key->endsWith("_{$predefinedPort}") && $command?->value() === 'FQDN' && $resource->server->proxyType() === 'CADDY') {
|
||||
$fqdns_exploded = str($savedService->fqdn)->explode(',');
|
||||
if ($fqdns_exploded->count() > 1) {
|
||||
continue;
|
||||
@@ -2038,7 +2044,7 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
|
||||
}
|
||||
} else {
|
||||
$generatedValue = generateEnvValue($command, $resource);
|
||||
if (! $foundEnv) {
|
||||
if (!$foundEnv) {
|
||||
EnvironmentVariable::create([
|
||||
'key' => $key,
|
||||
'value' => $generatedValue,
|
||||
@@ -2093,7 +2099,7 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
|
||||
}
|
||||
$defaultLabels = defaultLabels($resource->id, $containerName, type: 'service', subType: $isDatabase ? 'database' : 'application', subId: $savedService->id);
|
||||
$serviceLabels = $serviceLabels->merge($defaultLabels);
|
||||
if (! $isDatabase && $fqdns->count() > 0) {
|
||||
if (!$isDatabase && $fqdns->count() > 0) {
|
||||
if ($fqdns) {
|
||||
$shouldGenerateLabelsExactly = $resource->server->settings->generate_exact_labels;
|
||||
if ($shouldGenerateLabelsExactly) {
|
||||
@@ -2161,7 +2167,7 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
|
||||
}
|
||||
data_set($service, 'labels', $serviceLabels->toArray());
|
||||
data_forget($service, 'is_database');
|
||||
if (! data_get($service, 'restart')) {
|
||||
if (!data_get($service, 'restart')) {
|
||||
data_set($service, 'restart', RESTART_MODE);
|
||||
}
|
||||
if (data_get($service, 'restart') === 'no' || data_get($service, 'exclude_from_hc')) {
|
||||
@@ -2200,21 +2206,21 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
|
||||
$parsedServiceVariables->put('COOLIFY_CONTAINER_NAME', "$serviceName-{$resource->uuid}");
|
||||
|
||||
// TODO: move this in a shared function
|
||||
if (! $parsedServiceVariables->has('COOLIFY_APP_NAME')) {
|
||||
if (!$parsedServiceVariables->has('COOLIFY_APP_NAME')) {
|
||||
$parsedServiceVariables->put('COOLIFY_APP_NAME', "\"{$resource->name}\"");
|
||||
}
|
||||
if (! $parsedServiceVariables->has('COOLIFY_SERVER_IP')) {
|
||||
if (!$parsedServiceVariables->has('COOLIFY_SERVER_IP')) {
|
||||
$parsedServiceVariables->put('COOLIFY_SERVER_IP', "\"{$resource->destination->server->ip}\"");
|
||||
}
|
||||
if (! $parsedServiceVariables->has('COOLIFY_ENVIRONMENT_NAME')) {
|
||||
if (!$parsedServiceVariables->has('COOLIFY_ENVIRONMENT_NAME')) {
|
||||
$parsedServiceVariables->put('COOLIFY_ENVIRONMENT_NAME', "\"{$resource->environment->name}\"");
|
||||
}
|
||||
if (! $parsedServiceVariables->has('COOLIFY_PROJECT_NAME')) {
|
||||
if (!$parsedServiceVariables->has('COOLIFY_PROJECT_NAME')) {
|
||||
$parsedServiceVariables->put('COOLIFY_PROJECT_NAME', "\"{$resource->project()->name}\"");
|
||||
}
|
||||
|
||||
$parsedServiceVariables = $parsedServiceVariables->map(function ($value, $key) use ($envs_from_coolify) {
|
||||
if (! str($value)->startsWith('$')) {
|
||||
if (!str($value)->startsWith('$')) {
|
||||
$found_env = $envs_from_coolify->where('key', $key)->first();
|
||||
if ($found_env) {
|
||||
return $found_env->value;
|
||||
@@ -2298,7 +2304,7 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
|
||||
if ($serviceLabels->count() > 0) {
|
||||
$removedLabels = collect([]);
|
||||
$serviceLabels = $serviceLabels->filter(function ($serviceLabel, $serviceLabelName) use ($removedLabels) {
|
||||
if (! str($serviceLabel)->contains('=')) {
|
||||
if (!str($serviceLabel)->contains('=')) {
|
||||
$removedLabels->put($serviceLabelName, $serviceLabel);
|
||||
|
||||
return false;
|
||||
@@ -2318,11 +2324,11 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
|
||||
$serviceVolumes = $serviceVolumes->map(function ($volume) use ($resource, $topLevelVolumes, $pull_request_id) {
|
||||
if (is_string($volume)) {
|
||||
$volume = str($volume);
|
||||
if ($volume->contains(':') && ! $volume->startsWith('/')) {
|
||||
if ($volume->contains(':') && !$volume->startsWith('/')) {
|
||||
$name = $volume->before(':');
|
||||
$mount = $volume->after(':');
|
||||
if ($name->startsWith('.') || $name->startsWith('~')) {
|
||||
$dir = base_configuration_dir().'/applications/'.$resource->uuid;
|
||||
$dir = base_configuration_dir() . '/applications/' . $resource->uuid;
|
||||
if ($name->startsWith('.')) {
|
||||
$name = $name->replaceFirst('.', $dir);
|
||||
}
|
||||
@@ -2330,12 +2336,12 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
|
||||
$name = $name->replaceFirst('~', $dir);
|
||||
}
|
||||
if ($pull_request_id !== 0) {
|
||||
$name = $name."-pr-$pull_request_id";
|
||||
$name = $name . "-pr-$pull_request_id";
|
||||
}
|
||||
$volume = str("$name:$mount");
|
||||
} else {
|
||||
if ($pull_request_id !== 0) {
|
||||
$name = $name."-pr-$pull_request_id";
|
||||
$name = $name . "-pr-$pull_request_id";
|
||||
$volume = str("$name:$mount");
|
||||
if ($topLevelVolumes->has($name)) {
|
||||
$v = $topLevelVolumes->get($name);
|
||||
@@ -2374,7 +2380,7 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
|
||||
$name = $volume->before(':');
|
||||
$mount = $volume->after(':');
|
||||
if ($pull_request_id !== 0) {
|
||||
$name = $name."-pr-$pull_request_id";
|
||||
$name = $name . "-pr-$pull_request_id";
|
||||
}
|
||||
$volume = str("$name:$mount");
|
||||
}
|
||||
@@ -2385,7 +2391,7 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
|
||||
$read_only = data_get($volume, 'read_only');
|
||||
if ($source && $target) {
|
||||
if ((str($source)->startsWith('.') || str($source)->startsWith('~'))) {
|
||||
$dir = base_configuration_dir().'/applications/'.$resource->uuid;
|
||||
$dir = base_configuration_dir() . '/applications/' . $resource->uuid;
|
||||
if (str($source, '.')) {
|
||||
$source = str($source)->replaceFirst('.', $dir);
|
||||
}
|
||||
@@ -2393,23 +2399,23 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
|
||||
$source = str($source)->replaceFirst('~', $dir);
|
||||
}
|
||||
if ($pull_request_id !== 0) {
|
||||
$source = $source."-pr-$pull_request_id";
|
||||
$source = $source . "-pr-$pull_request_id";
|
||||
}
|
||||
if ($read_only) {
|
||||
data_set($volume, 'source', $source.':'.$target.':ro');
|
||||
data_set($volume, 'source', $source . ':' . $target . ':ro');
|
||||
} else {
|
||||
data_set($volume, 'source', $source.':'.$target);
|
||||
data_set($volume, 'source', $source . ':' . $target);
|
||||
}
|
||||
} else {
|
||||
if ($pull_request_id !== 0) {
|
||||
$source = $source."-pr-$pull_request_id";
|
||||
$source = $source . "-pr-$pull_request_id";
|
||||
}
|
||||
if ($read_only) {
|
||||
data_set($volume, 'source', $source.':'.$target.':ro');
|
||||
data_set($volume, 'source', $source . ':' . $target . ':ro');
|
||||
} else {
|
||||
data_set($volume, 'source', $source.':'.$target);
|
||||
data_set($volume, 'source', $source . ':' . $target);
|
||||
}
|
||||
if (! str($source)->startsWith('/')) {
|
||||
if (!str($source)->startsWith('/')) {
|
||||
if ($topLevelVolumes->has($source)) {
|
||||
$v = $topLevelVolumes->get($source);
|
||||
if (data_get($v, 'driver_opts.type') === 'cifs') {
|
||||
@@ -2442,11 +2448,11 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
|
||||
$serviceVolumes = $serviceVolumes->map(function ($volume) use ($resource, $topLevelVolumes, $pull_request_id) {
|
||||
if (is_string($volume)) {
|
||||
$volume = str($volume);
|
||||
if ($volume->contains(':') && ! $volume->startsWith('/')) {
|
||||
if ($volume->contains(':') && !$volume->startsWith('/')) {
|
||||
$name = $volume->before(':');
|
||||
$mount = $volume->after(':');
|
||||
if ($name->startsWith('.') || $name->startsWith('~')) {
|
||||
$dir = base_configuration_dir().'/applications/'.$resource->uuid;
|
||||
$dir = base_configuration_dir() . '/applications/' . $resource->uuid;
|
||||
if ($name->startsWith('.')) {
|
||||
$name = $name->replaceFirst('.', $dir);
|
||||
}
|
||||
@@ -2454,13 +2460,13 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
|
||||
$name = $name->replaceFirst('~', $dir);
|
||||
}
|
||||
if ($pull_request_id !== 0) {
|
||||
$name = $name."-pr-$pull_request_id";
|
||||
$name = $name . "-pr-$pull_request_id";
|
||||
}
|
||||
$volume = str("$name:$mount");
|
||||
} else {
|
||||
if ($pull_request_id !== 0) {
|
||||
$uuid = $resource->uuid;
|
||||
$name = $uuid."-$name-pr-$pull_request_id";
|
||||
$name = $uuid . "-$name-pr-$pull_request_id";
|
||||
$volume = str("$name:$mount");
|
||||
if ($topLevelVolumes->has($name)) {
|
||||
$v = $topLevelVolumes->get($name);
|
||||
@@ -2479,7 +2485,7 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
|
||||
}
|
||||
} else {
|
||||
$uuid = $resource->uuid;
|
||||
$name = str($uuid."-$name");
|
||||
$name = str($uuid . "-$name");
|
||||
$volume = str("$name:$mount");
|
||||
if ($topLevelVolumes->has($name->value())) {
|
||||
$v = $topLevelVolumes->get($name->value());
|
||||
@@ -2502,7 +2508,7 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
|
||||
$name = $volume->before(':');
|
||||
$mount = $volume->after(':');
|
||||
if ($pull_request_id !== 0) {
|
||||
$name = $name."-pr-$pull_request_id";
|
||||
$name = $name . "-pr-$pull_request_id";
|
||||
}
|
||||
$volume = str("$name:$mount");
|
||||
}
|
||||
@@ -2514,7 +2520,7 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
|
||||
if ($source && $target) {
|
||||
$uuid = $resource->uuid;
|
||||
if ((str($source)->startsWith('.') || str($source)->startsWith('~') || str($source)->startsWith('/'))) {
|
||||
$dir = base_configuration_dir().'/applications/'.$resource->uuid;
|
||||
$dir = base_configuration_dir() . '/applications/' . $resource->uuid;
|
||||
if (str($source, '.')) {
|
||||
$source = str($source)->replaceFirst('.', $dir);
|
||||
}
|
||||
@@ -2522,22 +2528,22 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
|
||||
$source = str($source)->replaceFirst('~', $dir);
|
||||
}
|
||||
if ($read_only) {
|
||||
data_set($volume, 'source', $source.':'.$target.':ro');
|
||||
data_set($volume, 'source', $source . ':' . $target . ':ro');
|
||||
} else {
|
||||
data_set($volume, 'source', $source.':'.$target);
|
||||
data_set($volume, 'source', $source . ':' . $target);
|
||||
}
|
||||
} else {
|
||||
if ($pull_request_id === 0) {
|
||||
$source = $uuid."-$source";
|
||||
$source = $uuid . "-$source";
|
||||
} else {
|
||||
$source = $uuid."-$source-pr-$pull_request_id";
|
||||
$source = $uuid . "-$source-pr-$pull_request_id";
|
||||
}
|
||||
if ($read_only) {
|
||||
data_set($volume, 'source', $source.':'.$target.':ro');
|
||||
data_set($volume, 'source', $source . ':' . $target . ':ro');
|
||||
} else {
|
||||
data_set($volume, 'source', $source.':'.$target);
|
||||
data_set($volume, 'source', $source . ':' . $target);
|
||||
}
|
||||
if (! str($source)->startsWith('/')) {
|
||||
if (!str($source)->startsWith('/')) {
|
||||
if ($topLevelVolumes->has($source)) {
|
||||
$v = $topLevelVolumes->get($source);
|
||||
if (data_get($v, 'driver_opts.type') === 'cifs') {
|
||||
@@ -2570,7 +2576,7 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
|
||||
|
||||
if ($pull_request_id !== 0 && count($serviceDependencies) > 0) {
|
||||
$serviceDependencies = $serviceDependencies->map(function ($dependency) use ($pull_request_id) {
|
||||
return $dependency."-pr-$pull_request_id";
|
||||
return $dependency . "-pr-$pull_request_id";
|
||||
});
|
||||
data_set($service, 'depends_on', $serviceDependencies->toArray());
|
||||
}
|
||||
@@ -2592,7 +2598,7 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
|
||||
$networkExists = $topLevelNetworks->contains(function ($value, $key) use ($networkName) {
|
||||
return $value == $networkName || $key == $networkName;
|
||||
});
|
||||
if (! $networkExists) {
|
||||
if (!$networkExists) {
|
||||
if (is_string($networkDetails) || is_int($networkDetails)) {
|
||||
$topLevelNetworks->put($networkDetails, null);
|
||||
}
|
||||
@@ -2620,7 +2626,7 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
|
||||
$definedNetworkExists = $topLevelNetworks->contains(function ($value, $_) use ($definedNetwork) {
|
||||
return $value == $definedNetwork;
|
||||
});
|
||||
if (! $definedNetworkExists) {
|
||||
if (!$definedNetworkExists) {
|
||||
foreach ($definedNetwork as $network) {
|
||||
if ($pull_request_id !== 0) {
|
||||
$topLevelNetworks->put($network, [
|
||||
@@ -2738,12 +2744,12 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
|
||||
'application_id' => $resource->id,
|
||||
])->first();
|
||||
['command' => $command, 'forService' => $forService, 'generatedValue' => $generatedValue, 'port' => $port] = parseEnvVariable($value);
|
||||
if (! is_null($command)) {
|
||||
if (!is_null($command)) {
|
||||
if ($command?->value() === 'FQDN' || $command?->value() === 'URL') {
|
||||
if (Str::lower($forService) === $serviceName) {
|
||||
$fqdn = generateFqdn($server, $containerName);
|
||||
} else {
|
||||
$fqdn = generateFqdn($server, Str::lower($forService).'-'.$resource->uuid);
|
||||
$fqdn = generateFqdn($server, Str::lower($forService) . '-' . $resource->uuid);
|
||||
}
|
||||
if ($port) {
|
||||
$fqdn = "$fqdn:$port";
|
||||
@@ -2764,7 +2770,7 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
|
||||
}
|
||||
} else {
|
||||
$generatedValue = generateEnvValue($command);
|
||||
if (! $foundEnv) {
|
||||
if (!$foundEnv) {
|
||||
EnvironmentVariable::create([
|
||||
'key' => $key,
|
||||
'value' => $generatedValue,
|
||||
@@ -2936,7 +2942,7 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
|
||||
}
|
||||
data_set($service, 'labels', $serviceLabels->toArray());
|
||||
data_forget($service, 'is_database');
|
||||
if (! data_get($service, 'restart')) {
|
||||
if (!data_get($service, 'restart')) {
|
||||
data_set($service, 'restart', RESTART_MODE);
|
||||
}
|
||||
data_set($service, 'container_name', $containerName);
|
||||
@@ -2947,7 +2953,7 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
|
||||
});
|
||||
if ($pull_request_id !== 0) {
|
||||
$services->each(function ($service, $serviceName) use ($pull_request_id, $services) {
|
||||
$services[$serviceName."-pr-$pull_request_id"] = $service;
|
||||
$services[$serviceName . "-pr-$pull_request_id"] = $service;
|
||||
data_forget($services, $serviceName);
|
||||
});
|
||||
}
|
||||
@@ -2975,7 +2981,7 @@ function newParser(Application|Service $resource, int $pull_request_id = 0, ?int
|
||||
|
||||
$uuid = data_get($resource, 'uuid');
|
||||
$compose = data_get($resource, 'docker_compose_raw');
|
||||
if (! $compose) {
|
||||
if (!$compose) {
|
||||
return collect([]);
|
||||
}
|
||||
|
||||
@@ -3368,29 +3374,29 @@ function newParser(Application|Service $resource, int $pull_request_id = 0, ?int
|
||||
$isDirectory = data_get($foundConfig, 'is_directory');
|
||||
} else {
|
||||
// if isDirectory is not set (or false) & content is also not set, we assume it is a directory
|
||||
if ((is_null($isDirectory) || ! $isDirectory) && is_null($content)) {
|
||||
if ((is_null($isDirectory) || !$isDirectory) && is_null($content)) {
|
||||
$isDirectory = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($type->value() === 'bind') {
|
||||
if ($source->value() === '/var/run/docker.sock') {
|
||||
$volume = $source->value().':'.$target->value();
|
||||
$volume = $source->value() . ':' . $target->value();
|
||||
} elseif ($source->value() === '/tmp' || $source->value() === '/tmp/') {
|
||||
$volume = $source->value().':'.$target->value();
|
||||
$volume = $source->value() . ':' . $target->value();
|
||||
} else {
|
||||
if ((int) $resource->compose_parsing_version >= 4) {
|
||||
if ($isApplication) {
|
||||
$mainDirectory = str(base_configuration_dir().'/applications/'.$uuid);
|
||||
$mainDirectory = str(base_configuration_dir() . '/applications/' . $uuid);
|
||||
} elseif ($isService) {
|
||||
$mainDirectory = str(base_configuration_dir().'/services/'.$uuid);
|
||||
$mainDirectory = str(base_configuration_dir() . '/services/' . $uuid);
|
||||
}
|
||||
} else {
|
||||
$mainDirectory = str(base_configuration_dir().'/applications/'.$uuid);
|
||||
$mainDirectory = str(base_configuration_dir() . '/applications/' . $uuid);
|
||||
}
|
||||
$source = replaceLocalSource($source, $mainDirectory);
|
||||
if ($isApplication && $isPullRequest) {
|
||||
$source = $source."-pr-$pullRequestId";
|
||||
$source = $source . "-pr-$pullRequestId";
|
||||
}
|
||||
LocalFileVolume::updateOrCreate(
|
||||
[
|
||||
@@ -3410,12 +3416,12 @@ function newParser(Application|Service $resource, int $pull_request_id = 0, ?int
|
||||
if (isDev()) {
|
||||
if ((int) $resource->compose_parsing_version >= 4) {
|
||||
if ($isApplication) {
|
||||
$source = $source->replace($mainDirectory, '/var/lib/docker/volumes/coolify_dev_coolify_data/_data/applications/'.$uuid);
|
||||
$source = $source->replace($mainDirectory, '/var/lib/docker/volumes/coolify_dev_coolify_data/_data/applications/' . $uuid);
|
||||
} elseif ($isService) {
|
||||
$source = $source->replace($mainDirectory, '/var/lib/docker/volumes/coolify_dev_coolify_data/_data/services/'.$uuid);
|
||||
$source = $source->replace($mainDirectory, '/var/lib/docker/volumes/coolify_dev_coolify_data/_data/services/' . $uuid);
|
||||
}
|
||||
} else {
|
||||
$source = $source->replace($mainDirectory, '/var/lib/docker/volumes/coolify_dev_coolify_data/_data/applications/'.$uuid);
|
||||
$source = $source->replace($mainDirectory, '/var/lib/docker/volumes/coolify_dev_coolify_data/_data/applications/' . $uuid);
|
||||
}
|
||||
}
|
||||
$volume = "$source:$target";
|
||||
@@ -3482,7 +3488,7 @@ function newParser(Application|Service $resource, int $pull_request_id = 0, ?int
|
||||
$depends_on = $newDependsOn;
|
||||
}
|
||||
}
|
||||
if (! $use_network_mode) {
|
||||
if (!$use_network_mode) {
|
||||
if ($topLevel->get('networks')?->count() > 0) {
|
||||
foreach ($topLevel->get('networks') as $networkName => $network) {
|
||||
if ($networkName === 'default') {
|
||||
@@ -3495,7 +3501,7 @@ function newParser(Application|Service $resource, int $pull_request_id = 0, ?int
|
||||
$networkExists = $networks->contains(function ($value, $key) use ($networkName) {
|
||||
return $value == $networkName || $key == $networkName;
|
||||
});
|
||||
if (! $networkExists) {
|
||||
if (!$networkExists) {
|
||||
$networks->put($networkName, null);
|
||||
}
|
||||
}
|
||||
@@ -3503,7 +3509,7 @@ function newParser(Application|Service $resource, int $pull_request_id = 0, ?int
|
||||
$baseNetworkExists = $networks->contains(function ($value, $_) use ($baseNetwork) {
|
||||
return $value == $baseNetwork;
|
||||
});
|
||||
if (! $baseNetworkExists) {
|
||||
if (!$baseNetworkExists) {
|
||||
foreach ($baseNetwork as $network) {
|
||||
$topLevel->get('networks')->put($network, [
|
||||
'name' => $network,
|
||||
@@ -3535,7 +3541,7 @@ function newParser(Application|Service $resource, int $pull_request_id = 0, ?int
|
||||
|
||||
$networks_temp = collect();
|
||||
|
||||
if (! $use_network_mode) {
|
||||
if (!$use_network_mode) {
|
||||
foreach ($networks as $key => $network) {
|
||||
if (gettype($network) === 'string') {
|
||||
// networks:
|
||||
@@ -3566,7 +3572,7 @@ function newParser(Application|Service $resource, int $pull_request_id = 0, ?int
|
||||
|
||||
$normalEnvironments = $environment->diffKeys($allMagicEnvironments);
|
||||
$normalEnvironments = $normalEnvironments->filter(function ($value, $key) {
|
||||
return ! str($value)->startsWith('SERVICE_');
|
||||
return !str($value)->startsWith('SERVICE_');
|
||||
});
|
||||
|
||||
foreach ($normalEnvironments as $key => $value) {
|
||||
@@ -3586,7 +3592,7 @@ function newParser(Application|Service $resource, int $pull_request_id = 0, ?int
|
||||
|
||||
continue;
|
||||
}
|
||||
if (! $value->startsWith('$')) {
|
||||
if (!$value->startsWith('$')) {
|
||||
continue;
|
||||
}
|
||||
if ($key->value() === $parsedValue->value()) {
|
||||
@@ -3717,7 +3723,7 @@ function newParser(Application|Service $resource, int $pull_request_id = 0, ?int
|
||||
$defaultLabels = defaultLabels($resource->id, $containerName, type: 'service', subType: $isDatabase ? 'database' : 'application', subId: $savedService->id);
|
||||
}
|
||||
// Add COOLIFY_FQDN & COOLIFY_URL to environment
|
||||
if (! $isDatabase && $fqdns instanceof Collection && $fqdns->count() > 0) {
|
||||
if (!$isDatabase && $fqdns instanceof Collection && $fqdns->count() > 0) {
|
||||
$coolifyEnvironments->put('COOLIFY_URL', $fqdns->implode(','));
|
||||
|
||||
$urls = $fqdns->map(function ($fqdn) {
|
||||
@@ -3729,7 +3735,7 @@ function newParser(Application|Service $resource, int $pull_request_id = 0, ?int
|
||||
|
||||
if ($environment->count() > 0) {
|
||||
$environment = $environment->filter(function ($value, $key) {
|
||||
return ! str($key)->startsWith('SERVICE_FQDN_');
|
||||
return !str($key)->startsWith('SERVICE_FQDN_');
|
||||
})->map(function ($value, $key) use ($resource) {
|
||||
// if value is empty, set it to null so if you set the environment variable in the .env file (Coolify's UI), it will used
|
||||
if (str($value)->isEmpty()) {
|
||||
@@ -3756,7 +3762,7 @@ function newParser(Application|Service $resource, int $pull_request_id = 0, ?int
|
||||
});
|
||||
}
|
||||
}
|
||||
if (! $isDatabase && $fqdns instanceof Collection && $fqdns->count() > 0) {
|
||||
if (!$isDatabase && $fqdns instanceof Collection && $fqdns->count() > 0) {
|
||||
if ($isApplication) {
|
||||
$shouldGenerateLabelsExactly = $resource->destination->server->settings->generate_exact_labels;
|
||||
$uuid = $resource->uuid;
|
||||
@@ -3849,7 +3855,7 @@ function newParser(Application|Service $resource, int $pull_request_id = 0, ?int
|
||||
'restart' => $restart->value(),
|
||||
'labels' => $serviceLabels,
|
||||
]);
|
||||
if (! $use_network_mode) {
|
||||
if (!$use_network_mode) {
|
||||
$payload['networks'] = $networks_temp;
|
||||
}
|
||||
if ($ports->count() > 0) {
|
||||
@@ -3909,7 +3915,7 @@ function isAssociativeArray($array)
|
||||
$array = $array->toArray();
|
||||
}
|
||||
|
||||
if (! is_array($array)) {
|
||||
if (!is_array($array)) {
|
||||
throw new \InvalidArgumentException('Input must be an array or a Collection.');
|
||||
}
|
||||
|
||||
@@ -4022,7 +4028,7 @@ function instanceSettings()
|
||||
function loadConfigFromGit(string $repository, string $branch, string $base_directory, int $server_id, int $team_id)
|
||||
{
|
||||
$server = Server::find($server_id)->where('team_id', $team_id)->first();
|
||||
if (! $server) {
|
||||
if (!$server) {
|
||||
return;
|
||||
}
|
||||
$uuid = new Cuid2;
|
||||
@@ -4049,7 +4055,7 @@ function loadConfigFromGit(string $repository, string $branch, string $base_dire
|
||||
|
||||
function loggy($message = null, array $context = [])
|
||||
{
|
||||
if (! isDev()) {
|
||||
if (!isDev()) {
|
||||
return;
|
||||
}
|
||||
if (function_exists('ray') && config('app.debug')) {
|
||||
@@ -4084,7 +4090,7 @@ function isEmailRateLimited(string $limiterKey, int $decaySeconds = 3600, ?calla
|
||||
$limiterKey,
|
||||
$maxAttempts = 0,
|
||||
function () use (&$rateLimited, &$limiterKey, $callbackOnSuccess) {
|
||||
isDev() && loggy('Rate limit not reached for '.$limiterKey);
|
||||
isDev() && loggy('Rate limit not reached for ' . $limiterKey);
|
||||
$rateLimited = false;
|
||||
|
||||
if ($callbackOnSuccess) {
|
||||
@@ -4093,8 +4099,8 @@ function isEmailRateLimited(string $limiterKey, int $decaySeconds = 3600, ?calla
|
||||
},
|
||||
$decaySeconds,
|
||||
);
|
||||
if (! $executed) {
|
||||
isDev() && loggy('Rate limit reached for '.$limiterKey.'. Rate limiter will be disabled for '.$decaySeconds.' seconds.');
|
||||
if (!$executed) {
|
||||
isDev() && loggy('Rate limit reached for ' . $limiterKey . '. Rate limiter will be disabled for ' . $decaySeconds . ' seconds.');
|
||||
$rateLimited = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('teams', function (Blueprint $table) {
|
||||
$table->boolean('slack_enabled')->default(false);
|
||||
$table->string('slack_webhook_url')->nullable();
|
||||
$table->boolean('slack_notifications_test')->default(true);
|
||||
$table->boolean('slack_notifications_deployments')->default(true);
|
||||
$table->boolean('slack_notifications_status_changes')->default(true);
|
||||
$table->boolean('slack_notifications_database_backups')->default(true);
|
||||
$table->boolean('slack_notifications_scheduled_tasks')->default(true);
|
||||
$table->boolean('slack_notifications_server_disk_usage')->default(true);
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('teams', function (Blueprint $table) {
|
||||
$table->dropColumn([
|
||||
'slack_enabled',
|
||||
'slack_webhook_url',
|
||||
'slack_notifications_test',
|
||||
'slack_notifications_deployments',
|
||||
'slack_notifications_status_changes',
|
||||
'slack_notifications_database_backups',
|
||||
'slack_notifications_scheduled_tasks',
|
||||
'slack_notifications_server_disk_usage',
|
||||
]);
|
||||
});
|
||||
}
|
||||
};
|
||||
11
public/svgs/heimdall.svg
Normal file
11
public/svgs/heimdall.svg
Normal file
@@ -0,0 +1,11 @@
|
||||
<svg width="346" height="434" xmlns="http://www.w3.org/2000/svg">
|
||||
|
||||
<g>
|
||||
<title>background</title>
|
||||
<rect fill="none" id="canvas_background" height="436" width="348" y="-1" x="-1"/>
|
||||
</g>
|
||||
<g>
|
||||
<title>Layer 1</title>
|
||||
<path id="svg_1" d="m221.227,397.119l0,-24.919l8.552,-26.577c59.868,-59.323 106.907,-114.847 115.221,-131.932l-10.215,3.56s9.5,-11.864 8.552,-18.51c-7.6,-116.273 -73.17,-176.862 -91.226,-191.732c26.607,31.955 19.326,42.4 19.326,42.4c-11.723,-21.833 -35.797,-43.032 -40.549,-48.409l-5.7,33.853c22.806,13.289 16.79,20.882 16.79,20.882c-5.387,-7.594 -19.957,-14.235 -19.957,-14.235l-1.586,9.491c6.973,3.165 38.327,22.149 38.565,70.872c0,59.323 -33.735,74.985 -39.674,77.357c-19.718,-11.865 -41.1,-11.627 -46.326,-12.1c-5.226,0.474 -26.608,0.236 -46.324,12.1c-5.941,-2.376 -39.676,-18.038 -39.676,-77.361c0.238,-48.723 31.6,-67.707 38.564,-70.872l-1.581,-9.487s-14.574,6.644 -19.955,14.238c0,0 -6.022,-7.593 16.786,-20.882l-5.702,-33.856c-4.752,5.378 -28.824,26.577 -40.543,48.407c0,0 -7.288,-10.443 19.32,-42.4c-18.054,14.87 -83.625,75.459 -91.226,191.732c-0.95,6.646 8.554,18.51 8.554,18.51l-10.217,-3.558c8.316,17.085 55.355,72.612 115.221,131.935l8.554,26.577l0,24.916l-7.6,-26.577l-17.345,-28.475l0,28.95c7.127,20.169 34.921,60.983 34.921,60.983l0,-84.475l-106.431,-116.037s66.52,54.1 85.526,73.56l9.978,-10.2c-12.355,-8.78 -66.045,-62.646 -70.8,-110.1c40.388,85.426 79.111,96.1 107.62,129.325l0,8.127l24.707,0l0,-8.131c28.508,-33.221 67.233,-43.9 107.619,-129.325c-4.75,47.459 -58.442,101.325 -70.8,110.1l9.976,10.2c19.006,-19.458 85.526,-73.56 85.526,-73.56l-106.423,116.041l0,84.475s27.8,-40.815 34.923,-60.984l0,-28.95l-17.343,28.475l-7.602,26.578zm-48.227,-102.036c-17.818,-11.865 -63.668,-39.629 -82.672,-83.053a314.117,314.117 0 0 0 34.447,34.882s-1.189,-30.61 1.186,-37.255c4.276,36.781 13.068,52.442 47.039,71.9c33.973,-19.458 42.763,-35.119 47.039,-71.9c2.375,6.645 1.188,37.255 1.188,37.255a314.117,314.117 0 0 0 34.447,-34.882c-19.006,43.424 -64.856,71.188 -82.674,83.053z" class="cls-1"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.0 KiB |
BIN
public/svgs/invoiceninja.png
Normal file
BIN
public/svgs/invoiceninja.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 35 KiB |
BIN
public/svgs/kuzzle.png
Normal file
BIN
public/svgs/kuzzle.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 16 KiB |
BIN
public/svgs/pairdrop.png
Normal file
BIN
public/svgs/pairdrop.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 60 KiB |
3
public/svgs/penpot.svg
Normal file
3
public/svgs/penpot.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 6.4 KiB |
BIN
public/svgs/whoogle.png
Normal file
BIN
public/svgs/whoogle.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 24 KiB |
@@ -15,6 +15,10 @@
|
||||
href="{{ route('notifications.discord') }}">
|
||||
<button>Discord</button>
|
||||
</a>
|
||||
<a class="{{ request()->routeIs('notifications.slack') ? 'dark:text-white' : '' }}"
|
||||
href="{{ route('notifications.slack') }}">
|
||||
<button>Slack</button>
|
||||
</a>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
43
resources/views/livewire/notifications/slack.blade.php
Normal file
43
resources/views/livewire/notifications/slack.blade.php
Normal file
@@ -0,0 +1,43 @@
|
||||
<div>
|
||||
<x-slot:title>
|
||||
Notifications | Coolify
|
||||
</x-slot>
|
||||
<x-notification.navbar />
|
||||
<form wire:submit='submit' class="flex flex-col gap-4">
|
||||
<div class="flex items-center gap-2">
|
||||
<h2>Slack</h2>
|
||||
<x-forms.button type="submit">
|
||||
Save
|
||||
</x-forms.button>
|
||||
@if ($slackEnabled)
|
||||
<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>
|
||||
@endif
|
||||
</div>
|
||||
<div class="w-32">
|
||||
<x-forms.checkbox instantSave="instantSaveSlackEnabled" id="slackEnabled" label="Enabled" />
|
||||
</div>
|
||||
<x-forms.input type="password"
|
||||
helper="Generate a webhook in Slack.<br>Example: https://hooks.slack.com/services/...." required
|
||||
id="slackWebhookUrl" label="Webhook" />
|
||||
</form>
|
||||
@if ($slackEnabled)
|
||||
<h2 class="mt-4">Subscribe to events</h2>
|
||||
<div class="w-64">
|
||||
@if (isDev())
|
||||
<x-forms.checkbox instantSave="saveModel" id="slackNotificationsTest" label="Test" />
|
||||
@endif
|
||||
<x-forms.checkbox instantSave="saveModel" id="slackNotificationsStatusChanges"
|
||||
label="Container Status Changes" />
|
||||
<x-forms.checkbox instantSave="saveModel" id="slackNotificationsDeployments"
|
||||
label="Application Deployments" />
|
||||
<x-forms.checkbox instantSave="saveModel" id="slackNotificationsDatabaseBackups" label="Backup Status" />
|
||||
<x-forms.checkbox instantSave="saveModel" id="slackNotificationsScheduledTasks"
|
||||
label="Scheduled Tasks Status" />
|
||||
<x-forms.checkbox instantSave="saveModel" id="slackNotificationsServerDiskUsage"
|
||||
label="Server Disk Usage" />
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
@@ -27,6 +27,13 @@
|
||||
helper="If set, all resources will only have docker container labels for {{ str($server->proxyType())->title() }}.<br>For applications, labels needs to be regenerated manually. <br>Resources needs to be restarted."
|
||||
id="server.settings.generate_exact_labels"
|
||||
label="Generate labels only for {{ str($server->proxyType())->title() }}" instantSave />
|
||||
<x-forms.checkbox instantSave="instantSaveRedirect" id="redirect_enabled"
|
||||
label="Override default request handler"
|
||||
helper="Requests to unknown hosts or stopped services will recieve a 503 response or be redirected to the URL you set below (need to enable this first)." />
|
||||
@if ($redirect_enabled)
|
||||
<x-forms.input placeholder="https://app.coolify.io" id="redirect_url"
|
||||
label="Redirect to (optional)" />
|
||||
@endif
|
||||
</div>
|
||||
@if ($server->proxyType() === ProxyTypes::TRAEFIK->value)
|
||||
<h4>Traefik</h4>
|
||||
@@ -40,8 +47,6 @@
|
||||
configurations.
|
||||
</div>
|
||||
@endif
|
||||
<x-forms.input placeholder="https://app.coolify.io" id="redirect_url" label="Default Redirect 404"
|
||||
helper="All urls that has no service available will be redirected to this domain." />
|
||||
<div wire:loading wire:target="loadProxyConfiguration" class="pt-4">
|
||||
<x-loading text="Loading proxy configuration..." />
|
||||
</div>
|
||||
|
||||
@@ -29,7 +29,8 @@
|
||||
@if (str_replace('|', '.', $fileName) === 'coolify.yaml' ||
|
||||
str_replace('|', '.', $fileName) === 'Caddyfile' ||
|
||||
str_replace('|', '.', $fileName) === 'coolify.caddy' ||
|
||||
str_replace('|', '.', $fileName) === 'default_redirect_404.caddy')
|
||||
str_replace('|', '.', $fileName) === 'default_redirect_503.yaml' ||
|
||||
str_replace('|', '.', $fileName) === 'default_redirect_503.caddy')
|
||||
<div>
|
||||
<h3 class="dark:text-white">File: {{ str_replace('|', '.', $fileName) }}</h3>
|
||||
</div>
|
||||
|
||||
@@ -13,6 +13,7 @@ use App\Livewire\ForcePasswordReset;
|
||||
use App\Livewire\Notifications\Discord as NotificationDiscord;
|
||||
use App\Livewire\Notifications\Email as NotificationEmail;
|
||||
use App\Livewire\Notifications\Telegram as NotificationTelegram;
|
||||
use App\Livewire\Notifications\Slack as NotificationSlack;
|
||||
use App\Livewire\Profile\Index as ProfileIndex;
|
||||
use App\Livewire\Project\Application\Configuration as ApplicationConfiguration;
|
||||
use App\Livewire\Project\Application\Deployment\Index as DeploymentIndex;
|
||||
@@ -132,6 +133,7 @@ Route::middleware(['auth', 'verified'])->group(function () {
|
||||
Route::get('/email', NotificationEmail::class)->name('notifications.email');
|
||||
Route::get('/telegram', NotificationTelegram::class)->name('notifications.telegram');
|
||||
Route::get('/discord', NotificationDiscord::class)->name('notifications.discord');
|
||||
Route::get('/slack', NotificationSlack::class)->name('notifications.slack');
|
||||
});
|
||||
|
||||
Route::prefix('storages')->group(function () {
|
||||
@@ -285,7 +287,7 @@ Route::middleware(['auth'])->group(function () {
|
||||
'privateKey' => $privateKeyLocation,
|
||||
'root' => '/',
|
||||
]);
|
||||
if (! $disk->exists($filename)) {
|
||||
if (!$disk->exists($filename)) {
|
||||
return response()->json(['message' => 'Backup not found.'], 404);
|
||||
}
|
||||
|
||||
@@ -297,7 +299,7 @@ Route::middleware(['auth'])->group(function () {
|
||||
if ($stream === false || is_null($stream)) {
|
||||
abort(500, 'Failed to open stream for the requested file.');
|
||||
}
|
||||
while (! feof($stream)) {
|
||||
while (!feof($stream)) {
|
||||
echo fread($stream, 2048);
|
||||
flush();
|
||||
}
|
||||
@@ -305,7 +307,7 @@ Route::middleware(['auth'])->group(function () {
|
||||
fclose($stream);
|
||||
}, 200, [
|
||||
'Content-Type' => 'application/octet-stream',
|
||||
'Content-Disposition' => 'attachment; filename="'.basename($filename).'"',
|
||||
'Content-Disposition' => 'attachment; filename="' . basename($filename) . '"',
|
||||
]);
|
||||
} catch (\Throwable $e) {
|
||||
return response()->json(['message' => $e->getMessage()], 500);
|
||||
|
||||
@@ -30,7 +30,7 @@ WARNING_SPACE=false
|
||||
|
||||
if [ "$TOTAL_SPACE" -lt "$REQUIRED_TOTAL_SPACE" ]; then
|
||||
WARNING_SPACE=true
|
||||
cat << 'EOF'
|
||||
cat << EOF
|
||||
WARNING: Insufficient total disk space!
|
||||
|
||||
Total disk space: ${TOTAL_SPACE}GB
|
||||
@@ -41,7 +41,7 @@ EOF
|
||||
fi
|
||||
|
||||
if [ "$AVAILABLE_SPACE" -lt "$REQUIRED_AVAILABLE_SPACE" ]; then
|
||||
cat << 'EOF'
|
||||
cat << EOF
|
||||
WARNING: Insufficient available disk space!
|
||||
|
||||
Available disk space: ${AVAILABLE_SPACE}GB
|
||||
@@ -49,7 +49,7 @@ Required available space: ${REQUIRED_AVAILABLE_SPACE}GB
|
||||
|
||||
==================
|
||||
EOF
|
||||
WARNING_SPACE=true
|
||||
WARNING_SPACE=true
|
||||
fi
|
||||
|
||||
if [ "$WARNING_SPACE" = true ]; then
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
# documentation: https://github.com/linuxserver/Heimdall
|
||||
# slogan: Heimdall is a dashboard for managing and organizing your server applications.
|
||||
# tags: dashboard, server, applications, interface
|
||||
# logo: svgs/heimdall.svg
|
||||
|
||||
services:
|
||||
heimdall:
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
# documentation: https://invoiceninja.github.io/selfhost.html
|
||||
# slogan: The leading open-source invoicing platform
|
||||
# tags: invoicing, billing, accounting, finance, self-hosted
|
||||
# logo: svgs/invoiceninja.png
|
||||
# port: 9000
|
||||
|
||||
services:
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
# documentation: https://kuzzle.io
|
||||
# slogan: Kuzzle is a generic backend offering the basic building blocks common to every application.
|
||||
# tags: backend, api, realtime, websocket, mqtt, rest, sdk, iot, geofencing, low-code
|
||||
# logo: svgs/kuzzle.png
|
||||
# port: 7512
|
||||
|
||||
services:
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
# documentation: https://pairdrop.net/
|
||||
# slogan: Pairdrop is a self-hosted file sharing and collaboration platform, offering secure file sharing and collaboration capabilities for efficient teamwork.
|
||||
# tags: file, sharing, collaboration, teamwork
|
||||
# logo: svgs/pairdrop.png
|
||||
# port: 3000
|
||||
|
||||
services:
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
# documentation: https://help.penpot.app/technical-guide/getting-started/#install-with-docker
|
||||
# slogan: Penpot is the first Open Source design and prototyping platform for product teams.
|
||||
# tags: penpot,design,prototyping,figma,open,source
|
||||
# logo: svgs/penpot.svg
|
||||
|
||||
services:
|
||||
frontend:
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
# port: 3000
|
||||
|
||||
x-common-env: &common-env
|
||||
PORT: 3030
|
||||
REMIX_APP_PORT: 3000
|
||||
NODE_ENV: production
|
||||
RUNTIME_PLATFORM: docker-compose
|
||||
@@ -118,7 +117,7 @@ services:
|
||||
environment:
|
||||
<<: *common-env
|
||||
PLATFORM_HOST: trigger
|
||||
PLATFORM_WS_PORT: 3030
|
||||
PLATFORM_WS_PORT: 3000
|
||||
SECURE_CONNECTION: "false"
|
||||
PLATFORM_SECRET: $PROVIDER_SECRET
|
||||
coordinator:
|
||||
@@ -133,7 +132,7 @@ services:
|
||||
environment:
|
||||
<<: *common-env
|
||||
PLATFORM_HOST: trigger
|
||||
PLATFORM_WS_PORT: 3030
|
||||
PLATFORM_WS_PORT: 3000
|
||||
SECURE_CONNECTION: "false"
|
||||
PLATFORM_SECRET: $COORDINATOR_SECRET
|
||||
healthcheck:
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
# documentation: https://github.com/benbusby/whoogle-search?tab=readme-ov-file
|
||||
# slogan: Whoogle is a self-hosted, privacy-focused search engine front-end for accessing Google search results without tracking and data collection.
|
||||
# tags: privacy, search engine
|
||||
# logo: svgs/whoogle.png
|
||||
# port: 5000
|
||||
|
||||
services:
|
||||
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user