Merge branch 'next' into fix-appwrite-template
This commit is contained in:
@@ -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";
|
||||||
|
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -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();
|
||||||
|
@@ -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) {
|
||||||
|
@@ -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(),
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@@ -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,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -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' => [
|
||||||
|
@@ -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"
|
||||||
|
@@ -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>
|
||||||
|
@@ -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
|
||||||
|
@@ -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"
|
||||||
|
@@ -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
@@ -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"
|
||||||
|
Reference in New Issue
Block a user