Compare commits

...

21 commits

Author SHA1 Message Date
peaklabs-dev
0cbb3a08c2
fix: use serverStatus not just status 2024-12-16 14:27:17 +01:00
peaklabs-dev
13f7d2a486
Merge branch 'fix-unreachable-notificiations' of https://github.com/peaklabs-dev/coolify into fix-unreachable-notificiations 2024-12-16 14:15:40 +01:00
peaklabs-dev
0957fe8bfc
chore: remove ray debugging 2024-12-16 14:15:38 +01:00
🏔️ Peak
48f73c732b
Merge branch 'next' into fix-unreachable-notificiations 2024-12-16 14:08:58 +01:00
peaklabs-dev
80425a16ee
fix: few fixes and use new ServerReachabilityChanged event 2024-12-16 14:06:53 +01:00
peaklabs-dev
5571aa4530
feat: use new ServerReachabilityChanged event instead of isDirty 2024-12-16 14:06:16 +01:00
peaklabs-dev
e30495a59b
feat: new ServerReachabilityChanged event 2024-12-16 14:05:17 +01:00
peaklabs-dev
1da3febd85
fix: remove duplicated ServerCheckJob call 2024-12-16 14:04:22 +01:00
Andras Bacsai
62574941df add 'admin' to allowed paths for unsubscribed accounts 2024-12-16 13:00:29 +01:00
Andras Bacsai
a0ad4bc90e better impersonate 2024-12-16 12:48:09 +01:00
🏔️ Peak
a3e53fc3aa
Merge pull request #4588 from ShadowArcanist/next
Added missing Discord Env for Outline one click service template
2024-12-16 12:31:02 +01:00
Andras Bacsai
f9d509bc4c enhance debug mode script to clear optimized classes after installing development dependencies 2024-12-16 12:19:07 +01:00
Andras Bacsai
e90df5a46b fix: debug mode 2024-12-16 12:14:36 +01:00
Andras Bacsai
e0e2126f38 fix: update Dockerfile entrypoint path to /etc/entrypoint.d 2024-12-16 12:08:30 +01:00
Andras Bacsai
52310fd21a add debug dev 2024-12-16 12:05:16 +01:00
Andras Bacsai
d58abd1998 refactor 2024-12-16 10:38:18 +01:00
Andras Bacsai
2565deba4f fix: resource clone name + volume name generation 2024-12-16 10:25:56 +01:00
Andras Bacsai
acf10f39f3 fix: compose loading seq 2024-12-16 10:19:11 +01:00
Andras Bacsai
65ba6b947a fix: oauthseeder 2024-12-16 09:48:41 +01:00
Andras Bacsai
c4cee62ff6 version++ 2024-12-16 09:45:47 +01:00
ShadowArcanist
46f99347b8 Added missing Discord Env for Outline one click service template 2024-12-15 22:34:09 +05:30
22 changed files with 141 additions and 114 deletions

View file

@ -2,6 +2,7 @@
namespace App\Console\Commands;
use App\Events\ServerReachabilityChanged;
use App\Models\Team;
use Illuminate\Console\Command;
@ -92,6 +93,8 @@ class CloudCleanupSubscriptions extends Command
$server->update([
'ip' => '1.2.3.4',
]);
ServerReachabilityChanged::dispatch($server);
}
}
}

View file

@ -0,0 +1,17 @@
<?php
namespace App\Events;
use App\Models\Server;
use Illuminate\Foundation\Events\Dispatchable;
class ServerReachabilityChanged
{
use Dispatchable;
public function __construct(
public readonly Server $server
) {
$this->server->isReachableChanged();
}
}

View file

@ -32,8 +32,6 @@ class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue
public Server $server;
public ScheduledDatabaseBackup $backup;
public StandalonePostgresql|StandaloneMongodb|StandaloneMysql|StandaloneMariadb|ServiceDatabase $database;
public ?string $container_name = null;
@ -58,10 +56,9 @@ class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue
public ?S3Storage $s3 = null;
public function __construct($backup)
public function __construct(public ScheduledDatabaseBackup $backup)
{
$this->onQueue('high');
$this->backup = $backup;
}
public function handle(): void

View file

@ -31,12 +31,7 @@ class ServerCheckJob implements ShouldBeEncrypted, ShouldQueue
return [(new WithoutOverlapping($this->server->uuid))->dontRelease()];
}
public function __construct(public Server $server)
{
if (isDev()) {
$this->handle();
}
}
public function __construct(public Server $server) {}
public function handle()
{

View file

@ -21,16 +21,28 @@ class Index extends Component
public function mount()
{
if (! isCloud()) {
if (! isCloud() && ! isDev()) {
return redirect()->route('dashboard');
}
if (Auth::id() !== 0) {
if (Auth::id() !== 0 && ! session('impersonating')) {
return redirect()->route('dashboard');
}
$this->getSubscribers();
}
public function back()
{
if (session('impersonating')) {
session()->forget('impersonating');
$user = User::find(0);
$team_to_switch_to = $user->teams->first();
Auth::login($user);
refreshSession($team_to_switch_to);
return redirect(request()->header('Referer'));
}
}
public function submitSearch()
{
if ($this->search !== '') {
@ -52,9 +64,10 @@ class Index extends Component
if (Auth::id() !== 0) {
return redirect()->route('dashboard');
}
session(['impersonating' => true]);
$user = User::find($user_id);
$team_to_switch_to = $user->teams->first();
Cache::forget("team:{$user->id}");
// Cache::forget("team:{$user->id}");
Auth::login($user);
refreshSession($team_to_switch_to);

View file

@ -327,7 +327,7 @@ class General extends Component
}
}
public function set_redirect()
public function setRedirect()
{
try {
$has_www = collect($this->application->fqdns)->filter(fn ($fqdn) => str($fqdn)->contains('www.'))->count();
@ -360,10 +360,10 @@ class General extends Component
if ($warning) {
$this->dispatch('warning', __('warning.sslipdomain'));
}
$this->resetDefaultLabels();
// $this->resetDefaultLabels();
if ($this->application->isDirty('redirect')) {
$this->set_redirect();
$this->setRedirect();
}
$this->checkFqdns();

View file

@ -9,11 +9,9 @@ class BackupNow extends Component
{
public $backup;
public function backup_now()
public function backupNow()
{
dispatch(new DatabaseBackupJob(
backup: $this->backup
));
DatabaseBackupJob::dispatch($this->backup);
$this->dispatch('success', 'Backup queued. It will be available in a few minutes.');
}
}

View file

@ -42,9 +42,11 @@ class ResourceOperations extends Component
$uuid = (string) new Cuid2;
$server = $new_destination->server;
if ($this->resource->getMorphClass() === \App\Models\Application::class) {
$name = 'clone-of-'.str($this->resource->name)->limit(20).'-'.$uuid;
$new_resource = $this->resource->replicate()->fill([
'uuid' => $uuid,
'name' => $this->resource->name.'-clone-'.$uuid,
'name' => $name,
'fqdn' => generateFqdn($server, $uuid),
'status' => 'exited',
'destination_id' => $new_destination->id,
@ -64,8 +66,12 @@ class ResourceOperations extends Component
}
$persistentVolumes = $this->resource->persistentStorages()->get();
foreach ($persistentVolumes as $volume) {
$volumeName = str($volume->name)->replace($this->resource->uuid, $new_resource->uuid)->value();
if ($volumeName === $volume->name) {
$volumeName = $new_resource->uuid.'-'.str($volume->name)->afterLast('-');
}
$newPersistentVolume = $volume->replicate()->fill([
'name' => $new_resource->uuid.'-'.str($volume->name)->afterLast('-'),
'name' => $volumeName,
'resource_id' => $new_resource->id,
]);
$newPersistentVolume->save();

View file

@ -4,6 +4,7 @@ namespace App\Livewire\Server;
use App\Actions\Server\StartSentinel;
use App\Actions\Server\StopSentinel;
use App\Events\ServerReachabilityChanged;
use App\Models\Server;
use Livewire\Attributes\Computed;
use Livewire\Attributes\Validate;
@ -202,6 +203,7 @@ class Show extends Component
$this->server->settings->is_reachable = $this->isReachable = true;
$this->server->settings->is_usable = $this->isUsable = true;
$this->server->settings->save();
ServerReachabilityChanged::dispatch($this->server);
$this->dispatch('proxyStatusUpdated');
} else {
$this->dispatch('error', 'Server is not reachable.', 'Please validate your configuration and connection.<br><br>Check this <a target="_blank" class="underline" href="https://coolify.io/docs/knowledge-base/server/openssh">documentation</a> for further help. <br><br>Error: '.$error);

View file

@ -6,6 +6,7 @@ use App\Actions\Proxy\StartProxy;
use App\Actions\Server\InstallDocker;
use App\Actions\Server\StartSentinel;
use App\Enums\ProxyTypes;
use App\Events\ServerReachabilityChanged;
use App\Jobs\CheckAndStartSentinelJob;
use App\Notifications\Server\Reachable;
use App\Notifications\Server\Unreachable;
@ -1025,57 +1026,36 @@ $schema://$host {
$unreachableNotificationSent = (bool) $this->unreachable_notification_sent;
$isReachable = (bool) $this->settings->is_reachable;
ray('Server Reachability Check', [
'server_id' => $this->id,
'unreachable_notification_sent' => $unreachableNotificationSent,
'is_reachable' => $isReachable,
'unreachable_count' => $this->unreachable_count,
]);
if ($isReachable === true) {
ray('Server is reachable - resetting counts');
$this->unreachable_count = 0;
$this->save();
if ($unreachableNotificationSent === true) {
ray('Sending reachable notification');
$this->sendReachableNotification();
}
return;
}
ray('Server is not reachable - incrementing count');
$this->increment('unreachable_count');
ray('Unreachable count: '.$this->unreachable_count);
if ($this->unreachable_count === 1) {
$this->settings->is_reachable = true;
$this->settings->saveQuietly();
$this->settings->save();
return;
}
if ($this->unreachable_count >= 2 && ! $unreachableNotificationSent) {
ray('Starting additional checks', [
'unreachable_count' => $this->unreachable_count,
]);
$failedChecks = 0;
for ($i = 0; $i < 2; $i++) {
for ($i = 0; $i < 3; $i++) {
$status = $this->serverStatus();
sleep(5);
$status = $this->status();
ray('Check attempt '.($i + 1), ['status' => $status]);
if (! $status) {
$failedChecks++;
}
}
ray('Additional checks complete', [
'failed_checks' => $failedChecks,
'will_send_notification' => ($failedChecks === 2 && ! $unreachableNotificationSent),
]);
if ($failedChecks === 2 && ! $unreachableNotificationSent) {
ray('Sending unreachable notification');
if ($failedChecks === 3 && ! $unreachableNotificationSent) {
$this->sendUnreachableNotification();
}
}
@ -1109,6 +1089,7 @@ $schema://$host {
if ($this->settings->is_reachable === false) {
$this->settings->is_reachable = true;
$this->settings->save();
ServerReachabilityChanged::dispatch($this);
}
return ['uptime' => true, 'error' => null];
@ -1119,6 +1100,7 @@ $schema://$host {
if ($this->settings->is_reachable === true) {
$this->settings->is_reachable = false;
$this->settings->save();
ServerReachabilityChanged::dispatch($this);
}
return ['uptime' => false, 'error' => $e->getMessage()];
@ -1209,6 +1191,7 @@ $schema://$host {
$this->settings->is_reachable = true;
$this->settings->is_usable = true;
$this->settings->save();
ServerReachabilityChanged::dispatch($this);
return true;
}

View file

@ -85,9 +85,6 @@ class ServerSetting extends Model
) {
$settings->server->restartSentinel();
}
if ($settings->isDirty('is_reachable')) {
$settings->server->isReachableChanged();
}
});
}

View file

@ -2,6 +2,7 @@
namespace App\Models;
use App\Events\ServerReachabilityChanged;
use App\Notifications\Channels\SendsDiscord;
use App\Notifications\Channels\SendsEmail;
use App\Notifications\Channels\SendsPushover;
@ -202,6 +203,7 @@ class Team extends Model implements SendsDiscord, SendsEmail, SendsPushover, Sen
'is_usable' => false,
'is_reachable' => false,
]);
ServerReachabilityChanged::dispatch($server);
}
}

View file

@ -69,6 +69,7 @@ function allowedPathsForUnsubscribedAccounts()
'logout',
'force-password-reset',
'livewire/update',
'admin',
];
}
function allowedPathsForBoardingAccounts()

View file

@ -2,7 +2,7 @@
return [
'coolify' => [
'version' => '4.0.0-beta.379',
'version' => '4.0.0-beta.380',
'self_hosted' => env('SELF_HOSTED', true),
'autoupdate' => env('AUTOUPDATE'),
'base_config_path' => env('BASE_CONFIG_PATH', '/data/coolify'),

View file

@ -4,6 +4,7 @@ namespace Database\Seeders;
use App\Models\OauthSetting;
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\Log;
class OauthSettingSeeder extends Seeder
{
@ -12,62 +13,53 @@ class OauthSettingSeeder extends Seeder
*/
public function run(): void
{
$providers = collect([
'azure',
'bitbucket',
'github',
'gitlab',
'google',
'authentik',
]);
try {
$providers = collect([
'azure',
'bitbucket',
'github',
'gitlab',
'google',
'authentik',
]);
$isOauthSeeded = OauthSetting::count() > 0;
$isOauthSeeded = OauthSetting::count() > 0;
// We changed how providers are defined in the database, so we authentik does not exists, we need to recreate all of the auth providers
// Before authentik was a provider, providers started with 0 id
// We changed how providers are defined in the database, so we authentik does not exists, we need to recreate all of the auth providers
// Before authentik was a provider, providers started with 0 id
$isOauthAuthentik = OauthSetting::where('provider', 'authentik')->exists();
if ($isOauthSeeded) {
if (! $isOauthAuthentik) {
$allProviders = OauthSetting::all();
$notFoundProviders = $providers->diff($allProviders->pluck('provider'));
$allProviders->each(function ($provider) {
$provider->delete();
});
$allProviders->each(function ($provider) use ($providers) {
$providerName = $provider->provider;
$foundProvider = $providers->first(function ($provider) use ($providerName) {
return $provider === $providerName;
});
if ($foundProvider) {
$newProvder = new OauthSetting;
$newProvder = $provider;
unset($newProvder->id);
$newProvder->save();
}
});
foreach ($notFoundProviders as $provider) {
OauthSetting::create([
'provider' => $provider,
]);
}
} else {
$isOauthAuthentik = OauthSetting::where('provider', 'authentik')->exists();
if (! $isOauthSeeded || $isOauthAuthentik) {
foreach ($providers as $provider) {
OauthSetting::updateOrCreate([
'provider' => $provider,
]);
}
return;
}
} else {
foreach ($providers as $provider) {
OauthSetting::updateOrCreate([
$allProviders = OauthSetting::all();
$notFoundProviders = $providers->diff($allProviders->pluck('provider'));
$allProviders->each(function ($provider) {
$provider->delete();
});
$allProviders->each(function ($provider) {
$provider = new OauthSetting;
$provider->provider = $provider->provider;
unset($provider->id);
$provider->save();
});
foreach ($notFoundProviders as $provider) {
OauthSetting::create([
'provider' => $provider,
]);
}
} catch (\Exception $e) {
Log::error($e->getMessage());
}
}
}

View file

@ -99,6 +99,9 @@ RUN mkdir -p /usr/local/bin && \
COPY docker/production/etc/php/conf.d/zzz-custom-php.ini /usr/local/etc/php/conf.d/zzz-custom-php.ini
ENV PHP_OPCACHE_ENABLE=1
# Configure entrypoint
COPY --chmod=755 docker/production/entrypoint.d/ /etc/entrypoint.d
# Copy application files from previous stages
COPY --from=base --chown=www-data:www-data /var/www/html/vendor ./vendor
COPY --from=static-assets --chown=www-data:www-data /app/public/build ./public/build

View file

@ -0,0 +1,8 @@
# Debug mode
if [ "$APP_DEBUG" = "true" ]; then
echo "Debug mode is enabled"
echo "Installing development dependencies..."
composer install --dev --no-scripts
echo "Clearing optimized classes..."
php artisan optimize:clear
fi

View file

@ -340,17 +340,19 @@
</li>
@endif
@if (isCloud() && isInstanceAdmin())
<li>
<a title="Admin" class="menu-item" href="/admin">
<svg class="text-pink-600 icon" viewBox="0 0 256 256"
xmlns="http://www.w3.org/2000/svg">
<path fill="currentColor"
d="M177.62 159.6a52 52 0 0 1-34 34a12.2 12.2 0 0 1-3.6.55a12 12 0 0 1-3.6-23.45a28 28 0 0 0 18.32-18.32a12 12 0 0 1 22.9 7.2ZM220 144a92 92 0 0 1-184 0c0-28.81 11.27-58.18 33.48-87.28a12 12 0 0 1 17.9-1.33l19.69 19.11L127 19.89a12 12 0 0 1 18.94-5.12C168.2 33.25 220 82.85 220 144m-24 0c0-41.71-30.61-78.39-52.52-99.29l-20.21 55.4a12 12 0 0 1-19.63 4.5L80.71 82.36C67 103.38 60 124.06 60 144a68 68 0 0 0 136 0" />
</svg>
Admin
</a>
</li>
@if (isCloud() || isDev())
@if (isInstanceAdmin() || session('impersonating'))
<li>
<a title="Admin" class="menu-item" href="/admin">
<svg class="text-pink-600 icon" viewBox="0 0 256 256"
xmlns="http://www.w3.org/2000/svg">
<path fill="currentColor"
d="M177.62 159.6a52 52 0 0 1-34 34a12.2 12.2 0 0 1-3.6.55a12 12 0 0 1-3.6-23.45a28 28 0 0 0 18.32-18.32a12 12 0 0 1 22.9 7.2ZM220 144a92 92 0 0 1-184 0c0-28.81 11.27-58.18 33.48-87.28a12 12 0 0 1 17.9-1.33l19.69 19.11L127 19.89a12 12 0 0 1 18.94-5.12C168.2 33.25 220 82.85 220 144m-24 0c0-41.71-30.61-78.39-52.52-99.29l-20.21 55.4a12 12 0 0 1-19.63 4.5L80.71 82.36C67 103.38 60 124.06 60 144a68 68 0 0 0 136 0" />
</svg>
Admin
</a>
</li>
@endif
@endif
<div class="flex-1"></div>
@if (isInstanceAdmin() && !isCloud())

View file

@ -1,7 +1,12 @@
<div>
<h1>Admin Dashboard</h1>
<h3 class="pt-4">Who am I now?</h3>
<div class="pb-4">{{ auth()->user()->name }}</div>
<div class="flex gap-2 pt-4">
<h3>Who am I now?</h3>
@if (session('impersonating'))
<x-forms.button wire:click="back">Go back to root</x-forms.button>
@endif
</div>
<div class="pb-4">{{ auth()->user()->name }} ({{ auth()->user()->email }})</div>
<form wire:submit="submitSearch" class="flex flex-col gap-2 lg:flex-row">
<x-forms.input wire:model="search" placeholder="Search for a user" />
<x-forms.button type="submit">Search</x-forms.button>

View file

@ -84,7 +84,7 @@
<option value="non-www">Redirect to non-www.</option>
</x-forms.select>
<x-modal-confirmation title="Confirm Redirection Setting?" buttonTitle="Set Direction"
submitAction="set_redirect" :actions="['All traffic will be redirected to the selected direction.']" confirmationText="{{ $application->fqdn . '/' }}"
submitAction="setRedirect" :actions="['All traffic will be redirected to the selected direction.']" confirmationText="{{ $application->fqdn . '/' }}"
confirmationLabel="Please confirm the execution of the action by entering the Application URL below"
shortConfirmationLabel="Application URL" :confirmWithPassword="false" step2ButtonText="Set Direction">
<x-slot:customButton>
@ -161,8 +161,7 @@
</div>
<div class="pt-1 text-xs">Nixpacks will detect the required configuration
automatically.
<a class="underline"
href="https://coolify.io/docs/applications">Framework
<a class="underline" href="https://coolify.io/docs/applications">Framework
Specific Docs</a>
</div>
@endif

View file

@ -57,6 +57,10 @@ services:
- GITHUB_APP_NAME=${GITHUB_APP_NAME}
- GITHUB_APP_ID=${GITHUB_APP_ID}
- GITHUB_APP_PRIVATE_KEY=${GITHUB_APP_PRIVATE_KEY}
- DISCORD_CLIENT_ID=${DISCORD_CLIENT_ID}
- DISCORD_CLIENT_SECRET=${DISCORD_CLIENT_SECRET}
- DISCORD_SERVER_ID=${DISCORD_SERVER_ID}
- DISCORD_SERVER_ROLES=${DISCORD_SERVER_ROLES}
- PGSSLMODE=${PGSSLMODE:-disable}
- FORCE_HTTPS=${FORCE_HTTPS:-true}
healthcheck:

View file

@ -1,10 +1,10 @@
{
"coolify": {
"v4": {
"version": "4.0.0-beta.379"
"version": "4.0.0-beta.380"
},
"nightly": {
"version": "4.0.0-beta.380"
"version": "4.0.0-beta.381"
},
"helper": {
"version": "1.0.4"