Merge branch 'next' into fix-appwrite-template

This commit is contained in:
Chirag Aggarwal
2025-09-04 17:04:33 +07:00
committed by GitHub
15 changed files with 117 additions and 22 deletions

View File

@@ -5,7 +5,7 @@ namespace App\Console\Commands;
use App\Enums\ActivityTypes; use App\Enums\ActivityTypes;
use App\Enums\ApplicationDeploymentStatus; use App\Enums\ApplicationDeploymentStatus;
use App\Jobs\CheckHelperImageJob; use App\Jobs\CheckHelperImageJob;
use App\Jobs\PullChangelogFromGitHub; use App\Jobs\PullChangelog;
use App\Models\ApplicationDeploymentQueue; use App\Models\ApplicationDeploymentQueue;
use App\Models\Environment; use App\Models\Environment;
use App\Models\ScheduledDatabaseBackup; use App\Models\ScheduledDatabaseBackup;
@@ -140,7 +140,7 @@ class Init extends Command
private function pullChangelogFromGitHub() private function pullChangelogFromGitHub()
{ {
try { try {
PullChangelogFromGitHub::dispatch(); PullChangelog::dispatch();
echo "Changelog fetch initiated\n"; echo "Changelog fetch initiated\n";
} catch (\Throwable $e) { } catch (\Throwable $e) {
echo "Could not fetch changelog from GitHub: {$e->getMessage()}\n"; echo "Could not fetch changelog from GitHub: {$e->getMessage()}\n";

View File

@@ -16,7 +16,7 @@ class SyncBunny extends Command
* *
* @var string * @var string
*/ */
protected $signature = 'sync:bunny {--templates} {--release} {--nightly}'; protected $signature = 'sync:bunny {--templates} {--release} {--github-releases} {--nightly}';
/** /**
* The console command description. * The console command description.
@@ -25,6 +25,50 @@ class SyncBunny extends Command
*/ */
protected $description = 'Sync files to BunnyCDN'; protected $description = 'Sync files to BunnyCDN';
/**
* Fetch GitHub releases and sync to CDN
*/
private function syncGitHubReleases($parent_dir, $bunny_cdn_storage_name, $bunny_cdn_path, $bunny_cdn)
{
$this->info('Fetching releases from GitHub...');
try {
$response = Http::timeout(30)
->get('https://api.github.com/repos/coollabsio/coolify/releases', [
'per_page' => 30, // Fetch more releases for better changelog
]);
if ($response->successful()) {
$releases = $response->json();
// Save releases to a temporary file
$releases_file = "$parent_dir/releases.json";
file_put_contents($releases_file, json_encode($releases, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
// Upload to CDN
Http::pool(fn (Pool $pool) => [
$pool->storage(fileName: $releases_file)->put("/$bunny_cdn_storage_name/$bunny_cdn_path/releases.json"),
$pool->purge("$bunny_cdn/coolify/releases.json"),
]);
// Clean up temporary file
unlink($releases_file);
$this->info('releases.json uploaded & purged...');
$this->info('Total releases synced: '.count($releases));
return true;
} else {
$this->error('Failed to fetch releases from GitHub: '.$response->status());
return false;
}
} catch (\Throwable $e) {
$this->error('Error fetching releases: '.$e->getMessage());
return false;
}
}
/** /**
* Execute the console command. * Execute the console command.
*/ */
@@ -33,6 +77,7 @@ class SyncBunny extends Command
$that = $this; $that = $this;
$only_template = $this->option('templates'); $only_template = $this->option('templates');
$only_version = $this->option('release'); $only_version = $this->option('release');
$only_github_releases = $this->option('github-releases');
$nightly = $this->option('nightly'); $nightly = $this->option('nightly');
$bunny_cdn = 'https://cdn.coollabs.io'; $bunny_cdn = 'https://cdn.coollabs.io';
$bunny_cdn_path = 'coolify'; $bunny_cdn_path = 'coolify';
@@ -90,7 +135,7 @@ class SyncBunny extends Command
$install_script_location = "$parent_dir/other/nightly/$install_script"; $install_script_location = "$parent_dir/other/nightly/$install_script";
$versions_location = "$parent_dir/other/nightly/$versions"; $versions_location = "$parent_dir/other/nightly/$versions";
} }
if (! $only_template && ! $only_version) { if (! $only_template && ! $only_version && ! $only_github_releases) {
if ($nightly) { if ($nightly) {
$this->info('About to sync files NIGHTLY (docker-compose.prod.yaml, upgrade.sh, install.sh, etc) to BunnyCDN.'); $this->info('About to sync files NIGHTLY (docker-compose.prod.yaml, upgrade.sh, install.sh, etc) to BunnyCDN.');
} else { } else {
@@ -128,12 +173,29 @@ class SyncBunny extends Command
if (! $confirmed) { if (! $confirmed) {
return; return;
} }
// First sync GitHub releases
$this->info('Syncing GitHub releases first...');
$this->syncGitHubReleases($parent_dir, $bunny_cdn_storage_name, $bunny_cdn_path, $bunny_cdn);
// Then sync versions.json
Http::pool(fn (Pool $pool) => [ Http::pool(fn (Pool $pool) => [
$pool->storage(fileName: $versions_location)->put("/$bunny_cdn_storage_name/$bunny_cdn_path/$versions"), $pool->storage(fileName: $versions_location)->put("/$bunny_cdn_storage_name/$bunny_cdn_path/$versions"),
$pool->purge("$bunny_cdn/$bunny_cdn_path/$versions"), $pool->purge("$bunny_cdn/$bunny_cdn_path/$versions"),
]); ]);
$this->info('versions.json uploaded & purged...'); $this->info('versions.json uploaded & purged...');
return;
} elseif ($only_github_releases) {
$this->info('About to sync GitHub releases to BunnyCDN.');
$confirmed = confirm('Are you sure you want to sync GitHub releases?');
if (! $confirmed) {
return;
}
// Use the reusable function
$this->syncGitHubReleases($parent_dir, $bunny_cdn_storage_name, $bunny_cdn_path, $bunny_cdn);
return; return;
} }

View File

@@ -6,7 +6,7 @@ use App\Jobs\CheckAndStartSentinelJob;
use App\Jobs\CheckForUpdatesJob; use App\Jobs\CheckForUpdatesJob;
use App\Jobs\CheckHelperImageJob; use App\Jobs\CheckHelperImageJob;
use App\Jobs\CleanupInstanceStuffsJob; use App\Jobs\CleanupInstanceStuffsJob;
use App\Jobs\PullChangelogFromGitHub; use App\Jobs\PullChangelog;
use App\Jobs\PullTemplatesFromCDN; use App\Jobs\PullTemplatesFromCDN;
use App\Jobs\RegenerateSslCertJob; use App\Jobs\RegenerateSslCertJob;
use App\Jobs\ScheduledJobManager; use App\Jobs\ScheduledJobManager;
@@ -68,7 +68,7 @@ class Kernel extends ConsoleKernel
$this->scheduleInstance->command('cleanup:unreachable-servers')->daily()->onOneServer(); $this->scheduleInstance->command('cleanup:unreachable-servers')->daily()->onOneServer();
$this->scheduleInstance->job(new PullTemplatesFromCDN)->cron($this->updateCheckFrequency)->timezone($this->instanceTimezone)->onOneServer(); $this->scheduleInstance->job(new PullTemplatesFromCDN)->cron($this->updateCheckFrequency)->timezone($this->instanceTimezone)->onOneServer();
$this->scheduleInstance->job(new PullChangelogFromGitHub)->cron($this->updateCheckFrequency)->timezone($this->instanceTimezone)->onOneServer(); $this->scheduleInstance->job(new PullChangelog)->cron($this->updateCheckFrequency)->timezone($this->instanceTimezone)->onOneServer();
$this->scheduleInstance->job(new CleanupInstanceStuffsJob)->everyTwoMinutes()->onOneServer(); $this->scheduleInstance->job(new CleanupInstanceStuffsJob)->everyTwoMinutes()->onOneServer();
$this->scheduleUpdates(); $this->scheduleUpdates();

View File

@@ -2284,6 +2284,9 @@ class ApplicationsController extends Controller
data_set($data, 'docker_compose_domains', json_encode($dockerComposeDomainsJson)); data_set($data, 'docker_compose_domains', json_encode($dockerComposeDomainsJson));
} }
$application->fill($data); $application->fill($data);
if ($application->settings->is_container_label_readonly_enabled && $requestHasDomains && $server->isProxyShouldRun()) {
$application->custom_labels = str(implode('|coolify|', generateLabelsApplication($application)))->replace('|coolify|', "\n");
}
$application->save(); $application->save();
if ($instantDeploy) { if ($instantDeploy) {

View File

@@ -11,8 +11,9 @@ use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels; use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\File; use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\Http; use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;
class PullChangelogFromGitHub implements ShouldBeEncrypted, ShouldQueue class PullChangelog implements ShouldBeEncrypted, ShouldQueue
{ {
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
@@ -26,21 +27,36 @@ class PullChangelogFromGitHub implements ShouldBeEncrypted, ShouldQueue
public function handle(): void public function handle(): void
{ {
try { try {
// Fetch from CDN instead of GitHub API to avoid rate limits
$cdnUrl = config('constants.coolify.releases_url');
$response = Http::retry(3, 1000) $response = Http::retry(3, 1000)
->timeout(30) ->timeout(30)
->get('https://api.github.com/repos/coollabsio/coolify/releases?per_page=10'); ->get($cdnUrl);
if ($response->successful()) { if ($response->successful()) {
$releases = $response->json(); $releases = $response->json();
// Limit to 10 releases for processing (same as before)
$releases = array_slice($releases, 0, 10);
$changelog = $this->transformReleasesToChangelog($releases); $changelog = $this->transformReleasesToChangelog($releases);
// Group entries by month and save them // Group entries by month and save them
$this->saveChangelogEntries($changelog); $this->saveChangelogEntries($changelog);
} else { } else {
send_internal_notification('PullChangelogFromGitHub failed with: '.$response->status().' '.$response->body()); // Log error instead of sending notification
Log::error('PullChangelogFromGitHub: Failed to fetch from CDN', [
'status' => $response->status(),
'url' => $cdnUrl,
]);
} }
} catch (\Throwable $e) { } catch (\Throwable $e) {
send_internal_notification('PullChangelogFromGitHub failed with: '.$e->getMessage()); // Log error instead of sending notification
Log::error('PullChangelogFromGitHub: Exception occurred', [
'message' => $e->getMessage(),
'trace' => $e->getTraceAsString(),
]);
} }
} }

View File

@@ -2,7 +2,7 @@
namespace App\Livewire; namespace App\Livewire;
use App\Jobs\PullChangelogFromGitHub; use App\Jobs\PullChangelog;
use App\Services\ChangelogService; use App\Services\ChangelogService;
use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Auth;
use Livewire\Component; use Livewire\Component;
@@ -23,6 +23,11 @@ class SettingsDropdown extends Component
return app(ChangelogService::class)->getEntriesForUser($user); return app(ChangelogService::class)->getEntriesForUser($user);
} }
public function getCurrentVersionProperty()
{
return 'v'.config('constants.coolify.version');
}
public function openWhatsNewModal() public function openWhatsNewModal()
{ {
$this->showWhatsNewModal = true; $this->showWhatsNewModal = true;
@@ -50,7 +55,7 @@ class SettingsDropdown extends Component
} }
try { try {
PullChangelogFromGitHub::dispatch(); PullChangelog::dispatch();
$this->dispatch('success', 'Changelog fetch initiated! Check back in a few moments.'); $this->dispatch('success', 'Changelog fetch initiated! Check back in a few moments.');
} catch (\Throwable $e) { } catch (\Throwable $e) {
$this->dispatch('error', 'Failed to fetch changelog: '.$e->getMessage()); $this->dispatch('error', 'Failed to fetch changelog: '.$e->getMessage());
@@ -62,6 +67,7 @@ class SettingsDropdown extends Component
return view('livewire.settings-dropdown', [ return view('livewire.settings-dropdown', [
'entries' => $this->entries, 'entries' => $this->entries,
'unreadCount' => $this->unreadCount, 'unreadCount' => $this->unreadCount,
'currentVersion' => $this->currentVersion,
]); ]);
} }
} }

View File

@@ -2,7 +2,7 @@
return [ return [
'coolify' => [ 'coolify' => [
'version' => '4.0.0-beta.426', 'version' => '4.0.0-beta.427',
'helper_version' => '1.0.10', 'helper_version' => '1.0.10',
'realtime_version' => '1.0.10', 'realtime_version' => '1.0.10',
'self_hosted' => env('SELF_HOSTED', true), 'self_hosted' => env('SELF_HOSTED', true),
@@ -12,6 +12,7 @@ return [
'helper_image' => env('HELPER_IMAGE', env('REGISTRY_URL', 'ghcr.io').'/coollabsio/coolify-helper'), 'helper_image' => env('HELPER_IMAGE', env('REGISTRY_URL', 'ghcr.io').'/coollabsio/coolify-helper'),
'realtime_image' => env('REALTIME_IMAGE', env('REGISTRY_URL', 'ghcr.io').'/coollabsio/coolify-realtime'), 'realtime_image' => env('REALTIME_IMAGE', env('REGISTRY_URL', 'ghcr.io').'/coollabsio/coolify-realtime'),
'is_windows_docker_desktop' => env('IS_WINDOWS_DOCKER_DESKTOP', false), 'is_windows_docker_desktop' => env('IS_WINDOWS_DOCKER_DESKTOP', false),
'releases_url' => 'https://cdn.coollabs.io/coolify/releases.json',
], ],
'urls' => [ 'urls' => [

View File

@@ -1,10 +1,10 @@
{ {
"coolify": { "coolify": {
"v4": { "v4": {
"version": "4.0.0-beta.426" "version": "4.0.0-beta.427"
}, },
"nightly": { "nightly": {
"version": "4.0.0-beta.427" "version": "4.0.0-beta.428"
}, },
"helper": { "helper": {
"version": "1.0.10" "version": "1.0.10"

View File

@@ -242,6 +242,9 @@
<p class="mt-1 text-sm dark:text-neutral-400"> <p class="mt-1 text-sm dark:text-neutral-400">
Stay up to date with the latest features and improvements. Stay up to date with the latest features and improvements.
</p> </p>
<p class="mt-1 text-xs dark:text-neutral-500">
Current version: <span class="font-semibold dark:text-neutral-300">{{ $currentVersion }}</span>
</p>
</div> </div>
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
@if (isDev()) @if (isDev())
@@ -299,6 +302,10 @@
<span x-text="entry.title"></span> <span x-text="entry.title"></span>
<x-external-link /> <x-external-link />
</a></span> </a></span>
<span x-show="entry.tag_name === '{{ $currentVersion }}'"
class="px-2 py-1 text-xs font-semibold bg-success text-white rounded-sm">
CURRENT VERSION
</span>
<span class="text-xs dark:text-neutral-400" <span class="text-xs dark:text-neutral-400"
x-text="new Date(entry.published_at).toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' })"></span> x-text="new Date(entry.published_at).toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' })"></span>
</div> </div>

View File

@@ -41,7 +41,7 @@
<h2>Invite New Member</h2> <h2>Invite New Member</h2>
@if (isInstanceAdmin()) @if (isInstanceAdmin())
<div class="pb-4 text-xs dark:text-warning">You need to configure (as root team) <a <div class="pb-4 text-xs dark:text-warning">You need to configure (as root team) <a
href="/settings#smtp" class="underline dark:text-warning">Transactional href="/settings/email" class="underline dark:text-warning">Transactional
Emails</a> Emails</a>
before before
you can invite a you can invite a

View File

@@ -12,7 +12,7 @@
</svg> </svg>
In progress In progress
</button> </button>
<button class="menu-item" @click="modalOpen=true" x-show="!showProgress"> <button class="menu-item cursor-pointer" @click="modalOpen=true" x-show="!showProgress">
<svg xmlns="http://www.w3.org/2000/svg" <svg xmlns="http://www.w3.org/2000/svg"
class="w-6 h-6 text-pink-500 transition-colors hover:text-pink-300" viewBox="0 0 24 24" class="w-6 h-6 text-pink-500 transition-colors hover:text-pink-300" viewBox="0 0 24 24"
stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round" stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round"

View File

@@ -18,7 +18,7 @@ services:
environment: environment:
- SERVICE_URL_OUTLINE_3000 - SERVICE_URL_OUTLINE_3000
- NODE_ENV=production - NODE_ENV=production
- SECRET_KEY=${SERVICE_BASE64_OUTLINE} - SECRET_KEY=${SERVICE_HEX_32_OUTLINE}
- UTILS_SECRET=${SERVICE_PASSWORD_64_OUTLINE} - UTILS_SECRET=${SERVICE_PASSWORD_64_OUTLINE}
- DATABASE_URL=postgres://${SERVICE_USER_POSTGRES}:${SERVICE_PASSWORD_64_POSTGRES}@postgres:5432/${POSTGRES_DATABASE:-outline} - DATABASE_URL=postgres://${SERVICE_USER_POSTGRES}:${SERVICE_PASSWORD_64_POSTGRES}@postgres:5432/${POSTGRES_DATABASE:-outline}
- REDIS_URL=redis://:${SERVICE_PASSWORD_64_REDIS}@redis:6379 - REDIS_URL=redis://:${SERVICE_PASSWORD_64_REDIS}@redis:6379

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1,10 +1,10 @@
{ {
"coolify": { "coolify": {
"v4": { "v4": {
"version": "4.0.0-beta.426" "version": "4.0.0-beta.427"
}, },
"nightly": { "nightly": {
"version": "4.0.0-beta.427" "version": "4.0.0-beta.428"
}, },
"helper": { "helper": {
"version": "1.0.10" "version": "1.0.10"