Compare commits

...

46 Commits

Author SHA1 Message Date
Andras Bacsai
5fddf01820 Merge pull request #2355 from coollabsio/next
v4.0.0-beta.295
2024-06-10 11:04:39 +02:00
Andras Bacsai
c80434141d fix: gitlab merge request should close PR 2024-06-10 10:42:52 +02:00
Andras Bacsai
d1128c7a1e fix: multiline variable should be literal + should be multiline in bash with \ 2024-06-09 22:37:23 +02:00
Andras Bacsai
aae81313a6 Refactor TelegramChannel to handle additional notification types 2024-06-09 21:44:13 +02:00
Andras Bacsai
4667f96b40 feat: db proxy logs 2024-06-09 21:33:17 +02:00
Andras Bacsai
28c320ae97 chore: Update install.sh script to version 1.3.2 and handle Linux Mint as Ubuntu 2024-06-09 15:52:23 +02:00
Andras Bacsai
45017efe00 rename migration 2024-06-07 18:16:42 +02:00
Andras Bacsai
a20290cac8 wip: new services based git apps 2024-06-07 17:21:46 +02:00
Andras Bacsai
31e02a154c refactor: Improve handling of Docker volumes in parseDockerComposeFile function 2024-06-07 17:06:27 +02:00
Andras Bacsai
023ee5db99 fix: Set default name for Docker volumes if it is null 2024-06-07 16:55:08 +02:00
Andras Bacsai
05d2e15ab5 update service-templates 2024-06-07 12:28:42 +02:00
Andras Bacsai
7d6590c60a Merge pull request #2347 from tikotzky/patch-1
Add GLITCHTIP_DOMAIN to glitchtip worker service
2024-06-07 12:28:21 +02:00
Andras Bacsai
3152ce183b Merge pull request #2374 from coollabsio/revert-2365-fix-navbar-scroll
Revert "Enhancement: Preserve scroll position in navbar to improve UX"
2024-06-07 12:25:37 +02:00
Andras Bacsai
d9f1a7c4d0 Revert "Enhancement: Preserve scroll position in navbar to improve UX" 2024-06-07 12:25:05 +02:00
Andras Bacsai
952aed3c49 Merge pull request #2365 from avila-gabriel/fix-navbar-scroll
Enhancement: Preserve scroll position in navbar to improve UX
2024-06-07 11:05:55 +02:00
Andras Bacsai
ab3c433450 Merge pull request #2352 from Geczy/patch-2
fix: supabase service, newest versions
2024-06-07 11:04:50 +02:00
Andras Bacsai
2b5e4a34d4 Merge pull request #2364 from TheLazyLemur/main
Add support  to install.sh for PopOS
2024-06-07 11:04:02 +02:00
Andras Bacsai
35cea852ca feat: add titles 2024-06-07 11:01:10 +02:00
Andras Bacsai
88581c8983 Update BUG_REPORT.yml 2024-06-07 10:28:43 +02:00
Andras Bacsai
a7a9aab189 feat: Add bounty program link to bug report template 2024-06-07 10:28:01 +02:00
Andras Bacsai
370c9b63cf fix: post deployment command could fail, but won't make the deployment fail anymore
feat: better error for post deployment command
2024-06-06 15:13:21 +02:00
Andras Bacsai
7cb08849de refactor: Improve pre and post deployment command inputs 2024-06-06 15:11:17 +02:00
Andras Bacsai
7f052163e3 fix: comment id should be string
fix: do not wait for GH response, stop preview before
2024-06-06 12:50:38 +02:00
Andras Bacsai
277d939033 services: rocketchat 2024-06-06 11:39:23 +02:00
Andras Bacsai
26fbdcfab0 Merge pull request #2344 from LEstradioto/feat--add-rocketchat-template
feat: add rocketchat template
2024-06-06 11:37:50 +02:00
Andras Bacsai
6d63ba9d4d chore: Update supported OS list with almalinux 2024-06-06 11:36:51 +02:00
Andras Bacsai
8963f4fd62 refactor: Initialize null properties in Github Change component 2024-06-06 11:10:16 +02:00
Andras Bacsai
463021a9f3 refactor: Remove unused variables and improve code readability 2024-06-06 11:09:27 +02:00
Andras Bacsai
f71a8e9fef fix: sort backup executions 2024-06-06 10:46:19 +02:00
Dan Rousseau
2dd5be1b4e chore: Update install.sh to support PopOS 2024-06-06 09:48:02 +02:00
Gabriel Avila
608838045f Preserve scroll position in navbar after livewire update 2024-06-06 04:14:53 -03:00
Andras Bacsai
899d506faa disable internal notifications on the cloud 2024-06-05 15:34:25 +02:00
Andras Bacsai
21b3e3ea05 refactor: Update deployment previews heading to "Deployments" 2024-06-05 15:32:56 +02:00
Andras Bacsai
a68951541c fix: handle previously defined compose previews 2024-06-05 15:29:00 +02:00
Andras Bacsai
7fd0deedb1 feat: able to add several domains to compose based previews 2024-06-05 15:14:44 +02:00
Andras Bacsai
e9e12ad843 feat: able to change database passwords on the UI. It won't sync to the database. 2024-06-05 11:44:25 +02:00
Andras Bacsai
4fd3185d12 fix: backup executions view 2024-06-05 11:44:10 +02:00
Andras Bacsai
f5ccebfd41 early return 2024-06-05 11:21:02 +02:00
Andras Bacsai
294721eef9 fix: autoupdate process 2024-06-04 21:57:00 +02:00
Andras Bacsai
8af509992d fix: custom docker compose commands, add project dir if needed 2024-06-04 21:26:49 +02:00
Matt
11fccb8e89 fix: supabase service, newest versions 2024-06-04 07:58:40 -05:00
Andras Bacsai
1e126dd2c3 refactor: Update save_environment_variables method to use application's environment_variables instead of environment_variables_preview 2024-06-04 12:59:45 +02:00
Andras Bacsai
cfe2f889a4 refactor: Append utm_source parameter to documentation URL 2024-06-04 12:59:35 +02:00
Andras Bacsai
1bd76b0e07 chore: Update version numbers to 4.0.0-beta.295 2024-06-04 12:23:43 +02:00
Luan Estradioto
bc3bb78916 add rocketchat template 2024-06-03 18:12:47 -03:00
Mordy Tikotzky
0ebf5e49fb Add GLITCHTIP_DOMAIN to glitchtip worker service
Without it set on the worker emails send via the worker point to localhost instead of the correct domain.
2024-06-03 15:01:52 -04:00
121 changed files with 1051 additions and 281 deletions

View File

@@ -1,17 +1,28 @@
name: Bug report
description: Create a new bug report
description: 'Create a new bug report.'
title: '[Bug]: '
body:
- type: markdown
attributes:
value: >-
# 💎 Bounty program (with
[algora.io](https://console.algora.io/org/coollabsio/bounties/new))
If you would like to prioritize the issue resolution, you can add bounty
to this issue.
Click [here](https://console.algora.io/org/coollabsio/bounties/new) to
get started.
- type: textarea
attributes:
label: Description
description: A clear and concise description of the problem
validations:
required: true
- type: textarea
attributes:
label: Minimal Reproduction (if possible, example repository)
description: Please provide a step by step guide to reproduce the issue
description: Please provide a step by step guide to reproduce the issue.
validations:
required: true
- type: textarea

View File

@@ -38,9 +38,6 @@ class UpdateCoolify
}
$this->update();
} catch (\Throwable $e) {
ray('InstanceAutoUpdateJob failed');
ray($e->getMessage());
send_internal_notification('InstanceAutoUpdateJob failed: ' . $e->getMessage());
throw $e;
}
}
@@ -57,7 +54,6 @@ class UpdateCoolify
"curl -fsSL https://cdn.coollabs.io/coolify/upgrade.sh -o /data/coolify/source/upgrade.sh",
"bash /data/coolify/source/upgrade.sh $this->latestVersion"
], $this->server);
send_internal_notification("Instance updated from {$this->currentVersion} -> {$this->latestVersion}");
return;
}
}

View File

@@ -26,7 +26,6 @@ class ServicesGenerate extends Command
*/
public function handle()
{
// ray()->clearAll();
$files = array_diff(scandir(base_path('templates/compose')), ['.', '..']);
$files = array_filter($files, function ($file) {
return strpos($file, '.yaml') !== false;
@@ -63,6 +62,7 @@ class ServicesGenerate extends Command
$documentation = collect(preg_grep('/^# documentation:/', explode("\n", $content)))->values();
if ($documentation->count() > 0) {
$documentation = str($documentation[0])->after('# documentation:')->trim()->value();
$documentation = str($documentation)->append('?utm_source=coolify.io');
} else {
$documentation = 'https://coolify.io/docs';
}

View File

@@ -6,15 +6,12 @@ use App\Jobs\CheckLogDrainContainerJob;
use App\Jobs\CleanupInstanceStuffsJob;
use App\Jobs\DatabaseBackupJob;
use App\Jobs\ScheduledTaskJob;
use App\Jobs\InstanceAutoUpdateJob;
use App\Jobs\ContainerStatusJob;
use App\Jobs\PullCoolifyImageJob;
use App\Jobs\PullHelperImageJob;
use App\Jobs\PullSentinelImageJob;
use App\Jobs\PullTemplatesAndVersions;
use App\Jobs\PullTemplatesFromCDN;
use App\Jobs\PullVersionsFromCDN;
use App\Jobs\ServerStatusJob;
use App\Models\InstanceSettings;
use App\Models\ScheduledDatabaseBackup;
use App\Models\ScheduledTask;
use App\Models\Server;
@@ -32,37 +29,33 @@ class Kernel extends ConsoleKernel
// Instance Jobs
$schedule->command('horizon:snapshot')->everyMinute();
$schedule->job(new CleanupInstanceStuffsJob)->everyMinute()->onOneServer();
$schedule->job(new PullVersionsFromCDN)->everyTenMinutes()->onOneServer();
$schedule->job(new PullTemplatesFromCDN)->everyTwoHours()->onOneServer();
// $schedule->job(new CheckResaleLicenseJob)->hourly()->onOneServer();
// Server Jobs
$this->check_scheduled_backups($schedule);
$this->check_resources($schedule);
$this->check_scheduled_backups($schedule);
// $this->pull_helper_image($schedule);
$this->check_scheduled_tasks($schedule);
$schedule->command('uploads:clear')->everyTwoMinutes();
} else {
// Instance Jobs
$schedule->command('horizon:snapshot')->everyFiveMinutes();
$schedule->command('cleanup:unreachable-servers')->daily();
$schedule->job(new PullVersionsFromCDN)->everyTenMinutes()->onOneServer();
$schedule->job(new PullTemplatesFromCDN)->everyTwoHours()->onOneServer();
$schedule->job(new PullCoolifyImageJob)->everyTenMinutes()->onOneServer();
$schedule->job(new PullTemplatesFromCDN)->everyThirtyMinutes()->onOneServer();
$schedule->job(new CleanupInstanceStuffsJob)->everyTwoMinutes()->onOneServer();
// $schedule->job(new CheckResaleLicenseJob)->hourly()->onOneServer();
// Server Jobs
$this->instance_auto_update($schedule);
$this->check_scheduled_backups($schedule);
$this->check_resources($schedule);
$this->pull_helper_image($schedule);
$this->pull_images($schedule);
$this->check_scheduled_tasks($schedule);
$schedule->command('cleanup:database --yes')->daily();
$schedule->command('uploads:clear')->everyTwoMinutes();
}
}
private function pull_helper_image($schedule)
private function pull_images($schedule)
{
$servers = $this->all_servers->where('settings.is_usable', true)->where('settings.is_reachable', true)->where('ip', '!=', '1.2.3.4');
foreach ($servers as $server) {
@@ -93,16 +86,6 @@ class Kernel extends ConsoleKernel
$schedule->job(new ServerStatusJob($server))->everyMinute()->onOneServer();
}
}
private function instance_auto_update($schedule)
{
if (isDev() || isCloud()) {
return;
}
$settings = InstanceSettings::get();
if ($settings->is_auto_update_enabled) {
$schedule->job(new InstanceAutoUpdateJob)->everyTenMinutes()->onOneServer();
}
}
private function check_scheduled_backups($schedule)
{
$scheduled_backups = ScheduledDatabaseBackup::all();

View File

@@ -410,11 +410,12 @@ class Github extends Controller
if ($action === 'closed' || $action === 'close') {
$found = ApplicationPreview::where('application_id', $application->id)->where('pull_request_id', $pull_request_id)->first();
if ($found) {
$container_name = generateApplicationContainerName($application, $pull_request_id);
instant_remote_process(["docker rm -f $container_name"], $application->destination->server);
ApplicationPullRequestUpdateJob::dispatchSync(application: $application, preview: $found, status: ProcessStatus::CLOSED);
$found->delete();
$container_name = generateApplicationContainerName($application, $pull_request_id);
// ray('Stopping container: ' . $container_name);
instant_remote_process(["docker rm -f $container_name"], $application->destination->server);
$return_payloads->push([
'application' => $application->name,
'status' => 'success',
@@ -430,7 +431,6 @@ class Github extends Controller
}
}
}
ray($return_payloads);
return response($return_payloads);
} catch (Exception $e) {
ray($e->getMessage());

View File

@@ -202,7 +202,7 @@ class Gitlab extends Controller
]);
ray('Preview deployments disabled for ' . $application->name);
}
} else if ($action === 'closed' || $action === 'close') {
} else if ($action === 'closed' || $action === 'close' || $action === 'merge') {
$found = ApplicationPreview::where('application_id', $application->id)->where('pull_request_id', $pull_request_id)->first();
if ($found) {
$found->delete();

View File

@@ -113,7 +113,6 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
public $tries = 1;
public function __construct(int $application_deployment_queue_id)
{
ray()->clearAll();
$this->application_deployment_queue = ApplicationDeploymentQueue::find($application_deployment_queue_id);
$this->application = Application::find($this->application_deployment_queue->application_id);
$this->build_pack = data_get($this->application, 'build_pack');
@@ -290,7 +289,6 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
}
private function post_deployment()
{
if ($this->server->isProxyShouldRun()) {
GetContainersStatus::dispatch($this->server);
// dispatch(new ContainerStatusJob($this->server));
@@ -347,9 +345,15 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
}
if (data_get($this->application, 'docker_compose_custom_start_command')) {
$this->docker_compose_custom_start_command = $this->application->docker_compose_custom_start_command;
if (!str($this->docker_compose_custom_start_command)->contains('--project-directory')) {
$this->docker_compose_custom_start_command = str($this->docker_compose_custom_start_command)->replaceFirst('compose', 'compose --project-directory ' . $this->workdir)->value();
}
}
if (data_get($this->application, 'docker_compose_custom_build_command')) {
$this->docker_compose_custom_build_command = $this->application->docker_compose_custom_build_command;
if (!str($this->docker_compose_custom_build_command)->contains('--project-directory')) {
$this->docker_compose_custom_build_command = str($this->docker_compose_custom_build_command)->replaceFirst('compose', 'compose --project-directory ' . $this->workdir)->value();
}
}
if ($this->pull_request_id === 0) {
$this->application_deployment_queue->addLogEntry("Starting deployment of {$this->application->name} to {$this->server->name}.");
@@ -367,7 +371,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
$yaml = $composeFile = $this->application->docker_compose_raw;
$this->save_environment_variables();
} else {
$composeFile = $this->application->parseCompose(pull_request_id: $this->pull_request_id);
$composeFile = $this->application->parseCompose(pull_request_id: $this->pull_request_id, preview_id: data_get($this, 'preview.id'));
$this->save_environment_variables();
if (!is_null($this->env_filename)) {
$services = collect($composeFile['services']);
@@ -759,7 +763,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
if ($env->version === '4.0.0-beta.239') {
$real_value = $env->real_value;
} else {
if ($env->is_literal) {
if ($env->is_literal || $env->is_multiline) {
$real_value = '\'' . $real_value . '\'';
} else {
$real_value = escapeEnvVariables($env->real_value);
@@ -800,10 +804,11 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
if ($env->version === '4.0.0-beta.239') {
$real_value = $env->real_value;
} else {
if ($env->is_literal) {
if ($env->is_literal || $env->is_multiline) {
$real_value = '\'' . $real_value . '\'';
} else {
$real_value = escapeEnvVariables($env->real_value);
ray($real_value);
}
}
$envs->push($env->key . '=' . $real_value);
@@ -1944,11 +1949,17 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
if ($this->pull_request_id === 0) {
foreach ($this->application->build_environment_variables as $env) {
$value = escapeshellarg($env->real_value);
if (str($value)->contains("\n") && data_get($env, 'is_multiline') === true) {
$value = str_replace("\n", "\\\n", $value);
}
$this->build_args->push("--build-arg {$env->key}={$value}");
}
} else {
foreach ($this->application->build_environment_variables_preview as $env) {
$value = escapeshellarg($env->real_value);
if (str($value)->contains("\n") && data_get($env, 'is_multiline') === true) {
$value = str_replace("\n", "\\\n", $value);
}
$this->build_args->push("--build-arg {$env->key}={$value}");
}
}
@@ -1964,10 +1975,20 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
$dockerfile = collect(Str::of($this->saved_outputs->get('dockerfile'))->trim()->explode("\n"));
if ($this->pull_request_id === 0) {
foreach ($this->application->build_environment_variables as $env) {
$dockerfile->splice(1, 0, "ARG {$env->key}={$env->real_value}");
if (str($env->real_value)->contains("\n") && data_get($env, 'is_multiline') === true) {
$value = str_replace("\n", "\\\n", $env->real_value);
} else {
$value = $env->real_value;
}
$dockerfile->splice(1, 0, "ARG {$env->key}={$value}");
}
} else {
foreach ($this->application->build_environment_variables_preview as $env) {
if (str($env->real_value)->contains("\n") && data_get($env, 'is_multiline') === true) {
$value = str_replace("\n", "\\\n", $env->real_value);
} else {
$value = $env->real_value;
}
$dockerfile->splice(1, 0, "ARG {$env->key}={$env->real_value}");
}
}
@@ -1987,7 +2008,7 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
if ($containers->count() == 0) {
return;
}
$this->application_deployment_queue->addLogEntry("Executing pre-deployment command (see debug log for output).");
$this->application_deployment_queue->addLogEntry("Executing pre-deployment command (see debug log for output/errors).");
foreach ($containers as $container) {
$containerName = data_get($container, 'Names');
@@ -2010,6 +2031,7 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
if (empty($this->application->post_deployment_command)) {
return;
}
$this->application_deployment_queue->addLogEntry("----------------------------------------");
$this->application_deployment_queue->addLogEntry("Executing post-deployment command (see debug log for output).");
$containers = getCurrentApplicationContainerStatus($this->server, $this->application->id, $this->pull_request_id);
@@ -2018,11 +2040,20 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
if ($containers->count() == 1 || str_starts_with($containerName, $this->application->post_deployment_command_container . '-' . $this->application->uuid)) {
$cmd = "sh -c '" . str_replace("'", "'\''", $this->application->post_deployment_command) . "'";
$exec = "docker exec {$containerName} {$cmd}";
$this->execute_remote_command(
[
'command' => $exec, 'hidden' => true
],
);
try {
$this->execute_remote_command(
[
'command' => $exec, 'hidden' => true, 'save' => 'post-deployment-command-output'
],
);
} catch (Exception $e) {
$post_deployment_command_output = $this->saved_outputs->get('post-deployment-command-output');
if ($post_deployment_command_output) {
$this->application_deployment_queue->addLogEntry("Post-deployment command failed.");
$this->application_deployment_queue->addLogEntry($post_deployment_command_output, 'stderr');
}
}
return;
}
}

View File

@@ -31,6 +31,7 @@ class ApplicationPullRequestUpdateJob implements ShouldQueue, ShouldBeEncrypted
{
try {
if ($this->application->is_public_repository()) {
ray('Public repository. Skipping comment update.');
return;
}
if ($this->status === ProcessStatus::CLOSED) {
@@ -59,7 +60,7 @@ class ApplicationPullRequestUpdateJob implements ShouldQueue, ShouldBeEncrypted
}
} catch (\Throwable $e) {
ray($e);
throw $e;
return $e;
}
}

View File

@@ -80,9 +80,9 @@ class CheckLogDrainContainerJob implements ShouldQueue, ShouldBeEncrypted
}
}
} catch (\Throwable $e) {
send_internal_notification("CheckLogDrainContainerJob failed on ({$this->server->id}) with: " . $e->getMessage());
if (!isCloud()) send_internal_notification("CheckLogDrainContainerJob failed on ({$this->server->id}) with: " . $e->getMessage());
ray($e->getMessage());
handleError($e);
return handleError($e);
}
}
}

View File

@@ -0,0 +1,59 @@
<?php
namespace App\Jobs;
use App\Models\InstanceSettings;
use App\Models\Server;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldBeEncrypted;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\Http;
class PullCoolifyImageJob implements ShouldQueue, ShouldBeEncrypted
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public $timeout = 1000;
public function __construct()
{
}
public function handle(): void
{
try {
if (isDev() || isCloud()) {
return;
}
$server = Server::findOrFail(0);
$response = Http::retry(3, 1000)->get('https://cdn.coollabs.io/coolify/versions.json');
if ($response->successful()) {
$versions = $response->json();
File::put(base_path('versions.json'), json_encode($versions, JSON_PRETTY_PRINT));
}
$latest_version = get_latest_version_of_coolify();
instant_remote_process(["docker pull -q ghcr.io/coollabsio/coolify:{$latest_version}"], $server, false);
$settings = InstanceSettings::get();
$current_version = config('version');
if (!$settings->is_auto_update_enabled) {
return;
}
if ($latest_version === $current_version) {
return;
}
if (version_compare($latest_version, $current_version, '<')) {
return;
}
instant_remote_process([
"curl -fsSL https://cdn.coollabs.io/coolify/upgrade.sh -o /data/coolify/source/upgrade.sh",
"bash /data/coolify/source/upgrade.sh $latest_version"
], $server);
} catch (\Throwable $e) {
throw $e;
}
}
}

View File

@@ -34,8 +34,7 @@ class PullVersionsFromCDN implements ShouldQueue, ShouldBeEncrypted
}
}
} catch (\Throwable $e) {
send_internal_notification('PullTemplatesAndVersions failed with: ' . $e->getMessage());
ray($e->getMessage());
throw $e;
}
}
}

View File

@@ -25,6 +25,7 @@ class Heading extends Component
return [
"echo-private:team.{$teamId},ApplicationStatusChanged" => 'check_status',
"compose_loaded" => '$refresh',
"update_links" => '$refresh',
];
}
public function mount()

View File

@@ -43,7 +43,7 @@ class Previews extends Component
try {
$success = true;
$preview = $this->application->previews->find($preview_id);
if (isset($preview->fqdn)) {
if (data_get_str($preview, 'fqdn')->isNotEmpty()) {
$preview->fqdn = str($preview->fqdn)->replaceEnd(',', '')->trim();
$preview->fqdn = str($preview->fqdn)->replaceStart(',', '')->trim();
$preview->fqdn = str($preview->fqdn)->trim()->lower();
@@ -79,7 +79,7 @@ class Previews extends Component
$random = new Cuid2(7);
$preview_fqdn = str_replace('{{random}}', $random, $template);
$preview_fqdn = str_replace('{{domain}}', $host, $preview_fqdn);
$preview_fqdn = str_replace('{{pr_id}}', $preview_id, $preview_fqdn);
$preview_fqdn = str_replace('{{pr_id}}', $preview->pull_request_id, $preview_fqdn);
$preview_fqdn = "$schema://$preview_fqdn";
$preview->fqdn = $preview_fqdn;
$preview->save();
@@ -88,17 +88,34 @@ class Previews extends Component
public function add(int $pull_request_id, string|null $pull_request_html_url = null)
{
try {
$this->setDeploymentUuid();
$found = ApplicationPreview::where('application_id', $this->application->id)->where('pull_request_id', $pull_request_id)->first();
if (!$found && !is_null($pull_request_html_url)) {
ApplicationPreview::create([
'application_id' => $this->application->id,
'pull_request_id' => $pull_request_id,
'pull_request_html_url' => $pull_request_html_url
]);
if ($this->application->build_pack === 'dockercompose') {
$this->setDeploymentUuid();
$found = ApplicationPreview::where('application_id', $this->application->id)->where('pull_request_id', $pull_request_id)->first();
if (!$found && !is_null($pull_request_html_url)) {
$found = ApplicationPreview::create([
'application_id' => $this->application->id,
'pull_request_id' => $pull_request_id,
'pull_request_html_url' => $pull_request_html_url,
'docker_compose_domains' => $this->application->docker_compose_domains,
]);
}
$found->generate_preview_fqdn_compose();
$this->application->refresh();
} else {
$this->setDeploymentUuid();
$found = ApplicationPreview::where('application_id', $this->application->id)->where('pull_request_id', $pull_request_id)->first();
if (!$found && !is_null($pull_request_html_url)) {
$found = ApplicationPreview::create([
'application_id' => $this->application->id,
'pull_request_id' => $pull_request_id,
'pull_request_html_url' => $pull_request_html_url,
]);
}
$this->application->generate_preview_fqdn($pull_request_id);
$this->application->refresh();
$this->dispatch('update_links');
$this->dispatch('success', 'Preview added.');
}
$this->application->generate_preview_fqdn($pull_request_id);
$this->application->refresh();
} catch (\Throwable $e) {
return handleError($e, $this);
}
@@ -152,7 +169,7 @@ class Previews extends Component
}
}
GetContainersStatus::dispatchSync($this->application->destination->server);
$this->application->refresh();
$this->dispatch('reloadWindow');
} catch (\Throwable $e) {
return handleError($e, $this);
}
@@ -172,15 +189,10 @@ class Previews extends Component
}
ApplicationPreview::where('application_id', $this->application->id)->where('pull_request_id', $pull_request_id)->first()->delete();
$this->application->refresh();
$this->dispatch('update_links');
$this->dispatch('success', 'Preview deleted.');
} catch (\Throwable $e) {
return handleError($e, $this);
}
}
public function previewRefresh()
{
$this->application->previews->each(function ($preview) {
$preview->refresh();
});
}
}

View File

@@ -0,0 +1,56 @@
<?php
namespace App\Livewire\Project\Application;
use App\Models\ApplicationPreview;
use Livewire\Component;
use Spatie\Url\Url;
use Visus\Cuid2\Cuid2;
class PreviewsCompose extends Component
{
public $service;
public $serviceName;
public ApplicationPreview $preview;
public function render()
{
return view('livewire.project.application.previews-compose');
}
public function save()
{
$domain = data_get($this->service, 'domain');
$docker_compose_domains = data_get($this->preview, 'docker_compose_domains');
$docker_compose_domains = json_decode($docker_compose_domains, true);
$docker_compose_domains[$this->serviceName]['domain'] = $domain;
$this->preview->docker_compose_domains = json_encode($docker_compose_domains);
$this->preview->save();
$this->dispatch('update_links');
$this->dispatch('success', 'Domain saved.');
}
public function generate()
{
$domains = collect(json_decode($this->preview->application->docker_compose_domains)) ?? collect();
$domain = $domains->first(function ($_, $key) {
return $key === $this->serviceName;
});
if ($domain) {
$domain = data_get($domain, 'domain');
$url = Url::fromString($domain);
$template = $this->preview->application->preview_url_template;
$host = $url->getHost();
$schema = $url->getScheme();
$random = new Cuid2(7);
$preview_fqdn = str_replace('{{random}}', $random, $template);
$preview_fqdn = str_replace('{{domain}}', $host, $preview_fqdn);
$preview_fqdn = str_replace('{{pr_id}}', $this->preview->pull_request_id, $preview_fqdn);
$preview_fqdn = "$schema://$preview_fqdn";
$docker_compose_domains = data_get($this->preview, 'docker_compose_domains');
$docker_compose_domains = json_decode($docker_compose_domains, true);
$docker_compose_domains[$this->serviceName]['domain'] = $this->service->domain = $preview_fqdn;
$this->preview->docker_compose_domains = json_encode($docker_compose_domains);
$this->preview->save();
}
$this->dispatch('update_links');
$this->dispatch('success', 'Domain generated.');
}
}

View File

@@ -7,7 +7,6 @@ use Livewire\Component;
class Index extends Component
{
public $database;
public $s3s;
public function mount()
{
$project = currentTeam()->load(['projects'])->projects->where('uuid', request()->route('project_uuid'))->first();
@@ -36,7 +35,6 @@ class Index extends Component
]);
}
$this->database = $database;
$this->s3s = currentTeam()->s3s;
}
public function render()
{

View File

@@ -50,7 +50,7 @@ class BackupExecutions extends Component
public function refreshBackupExecutions(): void
{
if ($this->backup) {
$this->executions = $this->backup->executions()->get()->sortByDesc('created_at');
$this->executions = $this->backup->executions()->get()->sortBy('created_at');
}
}
}

View File

@@ -4,12 +4,14 @@ namespace App\Livewire\Project\Database\Clickhouse;
use App\Actions\Database\StartDatabaseProxy;
use App\Actions\Database\StopDatabaseProxy;
use App\Models\Server;
use App\Models\StandaloneClickhouse;
use Exception;
use Livewire\Component;
class General extends Component
{
public Server $server;
public StandaloneClickhouse $database;
public ?string $db_url = null;
public ?string $db_url_public = null;
@@ -43,10 +45,11 @@ class General extends Component
if ($this->database->is_public) {
$this->db_url_public = $this->database->get_db_url();
}
$this->server = data_get($this->database,'destination.server');
}
public function instantSaveAdvanced() {
try {
if (!$this->database->destination->server->isLogDrainEnabled()) {
if (!$this->server->isLogDrainEnabled()) {
$this->database->is_log_drain_enabled = false;
$this->dispatch('error', 'Log drain is not enabled on the server. Please enable it first.');
return;

View File

@@ -25,6 +25,7 @@ class CreateScheduledBackup extends Component
];
public function mount()
{
$this->s3s = currentTeam()->s3s;
if ($this->s3s->count() > 0) {
$this->s3_storage_id = $this->s3s->first()->id;
}

View File

@@ -4,6 +4,7 @@ namespace App\Livewire\Project\Database\Dragonfly;
use App\Actions\Database\StartDatabaseProxy;
use App\Actions\Database\StopDatabaseProxy;
use App\Models\Server;
use App\Models\StandaloneDragonfly;
use Exception;
use Livewire\Component;
@@ -12,6 +13,7 @@ class General extends Component
{
protected $listeners = ['refresh'];
public Server $server;
public StandaloneDragonfly $database;
public ?string $db_url = null;
public ?string $db_url_public = null;
@@ -41,10 +43,11 @@ class General extends Component
if ($this->database->is_public) {
$this->db_url_public = $this->database->get_db_url();
}
$this->server = data_get($this->database,'destination.server');
}
public function instantSaveAdvanced() {
try {
if (!$this->database->destination->server->isLogDrainEnabled()) {
if (!$this->server->isLogDrainEnabled()) {
$this->database->is_log_drain_enabled = false;
$this->dispatch('error', 'Log drain is not enabled on the server. Please enable it first.');
return;

View File

@@ -4,6 +4,7 @@ namespace App\Livewire\Project\Database\Keydb;
use App\Actions\Database\StartDatabaseProxy;
use App\Actions\Database\StopDatabaseProxy;
use App\Models\Server;
use App\Models\StandaloneKeydb;
use Exception;
use Livewire\Component;
@@ -12,6 +13,7 @@ class General extends Component
{
protected $listeners = ['refresh'];
public Server $server;
public StandaloneKeydb $database;
public ?string $db_url = null;
public ?string $db_url_public = null;
@@ -43,10 +45,12 @@ class General extends Component
if ($this->database->is_public) {
$this->db_url_public = $this->database->get_db_url();
}
$this->server = data_get($this->database,'destination.server');
}
public function instantSaveAdvanced() {
try {
if (!$this->database->destination->server->isLogDrainEnabled()) {
if (!$this->server->isLogDrainEnabled()) {
$this->database->is_log_drain_enabled = false;
$this->dispatch('error', 'Log drain is not enabled on the server. Please enable it first.');
return;

View File

@@ -4,6 +4,7 @@ namespace App\Livewire\Project\Database\Mariadb;
use App\Actions\Database\StartDatabaseProxy;
use App\Actions\Database\StopDatabaseProxy;
use App\Models\Server;
use App\Models\StandaloneMariadb;
use Exception;
use Livewire\Component;
@@ -12,6 +13,7 @@ class General extends Component
{
protected $listeners = ['refresh'];
public Server $server;
public StandaloneMariadb $database;
public ?string $db_url = null;
public ?string $db_url_public = null;
@@ -50,10 +52,12 @@ class General extends Component
if ($this->database->is_public) {
$this->db_url_public = $this->database->get_db_url();
}
$this->server = data_get($this->database,'destination.server');
}
public function instantSaveAdvanced() {
try {
if (!$this->database->destination->server->isLogDrainEnabled()) {
if (!$this->server->isLogDrainEnabled()) {
$this->database->is_log_drain_enabled = false;
$this->dispatch('error', 'Log drain is not enabled on the server. Please enable it first.');
return;

View File

@@ -4,6 +4,7 @@ namespace App\Livewire\Project\Database\Mongodb;
use App\Actions\Database\StartDatabaseProxy;
use App\Actions\Database\StopDatabaseProxy;
use App\Models\Server;
use App\Models\StandaloneMongodb;
use Exception;
use Livewire\Component;
@@ -12,6 +13,7 @@ class General extends Component
{
protected $listeners = ['refresh'];
public Server $server;
public StandaloneMongodb $database;
public ?string $db_url = null;
public ?string $db_url_public = null;
@@ -48,11 +50,13 @@ class General extends Component
if ($this->database->is_public) {
$this->db_url_public = $this->database->get_db_url();
}
$this->server = data_get($this->database,'destination.server');
}
public function instantSaveAdvanced()
{
try {
if (!$this->database->destination->server->isLogDrainEnabled()) {
if (!$this->server->isLogDrainEnabled()) {
$this->database->is_log_drain_enabled = false;
$this->dispatch('error', 'Log drain is not enabled on the server. Please enable it first.');
return;

View File

@@ -4,6 +4,7 @@ namespace App\Livewire\Project\Database\Mysql;
use App\Actions\Database\StartDatabaseProxy;
use App\Actions\Database\StopDatabaseProxy;
use App\Models\Server;
use App\Models\StandaloneMysql;
use Exception;
use Livewire\Component;
@@ -13,6 +14,7 @@ class General extends Component
protected $listeners = ['refresh'];
public StandaloneMysql $database;
public Server $server;
public ?string $db_url = null;
public ?string $db_url_public = null;
@@ -50,11 +52,12 @@ class General extends Component
if ($this->database->is_public) {
$this->db_url_public = $this->database->get_db_url();
}
$this->server = data_get($this->database,'destination.server');
}
public function instantSaveAdvanced()
{
try {
if (!$this->database->destination->server->isLogDrainEnabled()) {
if (!$this->server->isLogDrainEnabled()) {
$this->database->is_log_drain_enabled = false;
$this->dispatch('error', 'Log drain is not enabled on the server. Please enable it first.');
return;

View File

@@ -4,6 +4,7 @@ namespace App\Livewire\Project\Database\Postgresql;
use App\Actions\Database\StartDatabaseProxy;
use App\Actions\Database\StopDatabaseProxy;
use App\Models\Server;
use App\Models\StandalonePostgresql;
use Exception;
use Livewire\Component;
@@ -13,6 +14,7 @@ use function Aws\filter;
class General extends Component
{
public StandalonePostgresql $database;
public Server $server;
public string $new_filename;
public string $new_content;
public ?string $db_url = null;
@@ -57,11 +59,12 @@ class General extends Component
if ($this->database->is_public) {
$this->db_url_public = $this->database->get_db_url();
}
$this->server = data_get($this->database,'destination.server');
}
public function instantSaveAdvanced()
{
try {
if (!$this->database->destination->server->isLogDrainEnabled()) {
if (!$this->server->isLogDrainEnabled()) {
$this->database->is_log_drain_enabled = false;
$this->dispatch('error', 'Log drain is not enabled on the server. Please enable it first.');
return;

View File

@@ -4,6 +4,7 @@ namespace App\Livewire\Project\Database\Redis;
use App\Actions\Database\StartDatabaseProxy;
use App\Actions\Database\StopDatabaseProxy;
use App\Models\Server;
use App\Models\StandaloneRedis;
use Exception;
use Livewire\Component;
@@ -12,6 +13,7 @@ class General extends Component
{
protected $listeners = ['refresh'];
public Server $server;
public StandaloneRedis $database;
public ?string $db_url = null;
public ?string $db_url_public = null;
@@ -43,10 +45,12 @@ class General extends Component
if ($this->database->is_public) {
$this->db_url_public = $this->database->get_db_url();
}
$this->server = data_get($this->database,'destination.server');
}
public function instantSaveAdvanced() {
try {
if (!$this->database->destination->server->isLogDrainEnabled()) {
if (!$this->server->isLogDrainEnabled()) {
$this->database->is_log_drain_enabled = false;
$this->dispatch('error', 'Log drain is not enabled on the server. Please enable it first.');
return;

View File

@@ -6,6 +6,7 @@ use App\Models\Application;
use App\Models\GithubApp;
use App\Models\GitlabApp;
use App\Models\Project;
use App\Models\Service;
use App\Models\StandaloneDocker;
use App\Models\SwarmDocker;
use Carbon\Carbon;
@@ -33,6 +34,8 @@ class PublicGitRepository extends Component
public $build_pack = 'nixpacks';
public bool $show_is_static = true;
public bool $new_compose_services = false;
protected $rules = [
'repository_url' => 'required|url',
'port' => 'required|numeric',
@@ -177,6 +180,31 @@ class PublicGitRepository extends Component
$project = Project::where('uuid', $project_uuid)->first();
$environment = $project->load(['environments'])->environments->where('name', $environment_name)->first();
if ($this->build_pack === 'dockercompose' && isDev() && $this->new_compose_services ) {
$server = $destination->server;
$new_service = [
'name' => 'service' . str()->random(10),
'docker_compose_raw' => 'coolify',
'environment_id' => $environment->id,
'server_id' => $server->id,
];
if ($this->git_source === 'other') {
$new_service['git_repository'] = $this->git_repository;
$new_service['git_branch'] = $this->git_branch;
} else {
$new_service['git_repository'] = $this->git_repository;
$new_service['git_branch'] = $this->git_branch;
$new_service['source_id'] = $this->git_source->id;
$new_service['source_type'] = $this->git_source->getMorphClass();
}
$service = Service::create($new_service);
return redirect()->route('project.service.configuration', [
'service_uuid' => $service->uuid,
'environment_name' => $environment->name,
'project_uuid' => $project->uuid,
]);
return;
}
if ($this->git_source === 'other') {
$application_init = [
'name' => generate_random_name(),

View File

@@ -10,6 +10,7 @@ use Livewire\Component;
class Create extends Component
{
public $type;
public $project;
public function mount()
{
$type = str(request()->query('type'));
@@ -20,6 +21,7 @@ class Create extends Component
if (!$project) {
return redirect()->route('dashboard');
}
$this->project = $project;
$environment = $project->load(['environments'])->environments->where('name', request()->route('environment_name'))->first();
if (!$environment) {
return redirect()->route('dashboard');

View File

@@ -11,16 +11,16 @@ use Livewire\Component;
class Change extends Component
{
public string $webhook_endpoint;
public ?string $ipv4;
public ?string $ipv6;
public ?string $fqdn;
public ?string $ipv4 = null;
public ?string $ipv6 = null;
public ?string $fqdn = null;
public ?bool $default_permissions = true;
public ?bool $preview_deployment_permissions = true;
public ?bool $administration = false;
public $parameters;
public ?GithubApp $github_app;
public ?GithubApp $github_app = null;
public string $name;
public bool $is_system_wide;

View File

@@ -861,14 +861,10 @@ class Application extends BaseModel
instant_remote_process($commands, $this->destination->server, false);
}
function parseCompose(int $pull_request_id = 0)
function parseCompose(int $pull_request_id = 0, ?int $preview_id = null)
{
if ($this->docker_compose_raw) {
$mainCompose = parseDockerComposeFile(resource: $this, isNew: false, pull_request_id: $pull_request_id);
if ($this->getMorphClass() === 'App\Models\Application' && $this->docker_compose_pr_raw) {
parseDockerComposeFile(resource: $this, isNew: false, pull_request_id: $pull_request_id, is_pr: true);
}
return $mainCompose;
return parseDockerComposeFile(resource: $this, isNew: false, pull_request_id: $pull_request_id, preview_id: $preview_id);
} else {
return collect([]);
}
@@ -1052,7 +1048,8 @@ class Application extends BaseModel
}
}
}
function generate_preview_fqdn(int $pull_request_id) {
function generate_preview_fqdn(int $pull_request_id)
{
$preview = ApplicationPreview::findPreviewByApplicationAndPullId($this->id, $pull_request_id);
if (is_null(data_get($preview, 'fqdn')) && $this->fqdn) {
if (str($this->fqdn)->contains(',')) {

View File

@@ -2,6 +2,9 @@
namespace App\Models;
use Spatie\Url\Url;
use Visus\Cuid2\Cuid2;
class ApplicationPreview extends BaseModel
{
protected $guarded = [];
@@ -34,4 +37,26 @@ class ApplicationPreview extends BaseModel
{
return $this->belongsTo(Application::class);
}
function generate_preview_fqdn_compose()
{
$domains = collect(json_decode($this->application->docker_compose_domains)) ?? collect();
foreach ($domains as $service_name => $domain) {
$domain = data_get($domain, 'domain');
$url = Url::fromString($domain);
$template = $this->application->preview_url_template;
$host = $url->getHost();
$schema = $url->getScheme();
$random = new Cuid2(7);
$preview_fqdn = str_replace('{{random}}', $random, $template);
$preview_fqdn = str_replace('{{domain}}', $host, $preview_fqdn);
$preview_fqdn = str_replace('{{pr_id}}', $this->pull_request_id, $preview_fqdn);
$preview_fqdn = "$schema://$preview_fqdn";
$docker_compose_domains = data_get($this, 'docker_compose_domains');
$docker_compose_domains = json_decode($docker_compose_domains, true);
$docker_compose_domains[$service_name]['domain'] = $preview_fqdn;
$docker_compose_domains = json_encode($docker_compose_domains);
$this->docker_compose_domains = $docker_compose_domains;
$this->save();
}
}
}

View File

@@ -22,6 +22,8 @@ class TelegramChannel
$topicId = data_get($notifiable, 'telegram_notifications_test_message_thread_id');
break;
case 'App\Notifications\Application\StatusChanged':
case 'App\Notifications\Container\ContainerRestarted':
case 'App\Notifications\Container\ContainerStopped':
$topicId = data_get($notifiable, 'telegram_notifications_status_changes_message_thread_id');
break;
case 'App\Notifications\Application\DeploymentSuccess':

View File

@@ -38,7 +38,7 @@ const SPECIFIC_SERVICES = [
// Based on /etc/os-release
const SUPPORTED_OS = [
'ubuntu debian raspbian',
'centos fedora rhel ol rocky amzn',
'centos fedora rhel ol rocky amzn almalinux',
'sles opensuse-leap opensuse-tumbleweed'
];

View File

@@ -488,8 +488,10 @@ function generateLabelsApplication(Application $application, ?ApplicationPreview
));
}
} else {
if ($preview->fqdn) {
if (data_get($preview,'fqdn')) {
$domains = Str::of(data_get($preview, 'fqdn'))->explode(',');
} else {
$domains = collect([]);
}
$labels = $labels->merge(fqdnLabelsForTraefik(
uuid: $appUuid,

View File

@@ -657,9 +657,8 @@ function getTopLevelNetworks(Service|Application $resource)
}
}
function parseDockerComposeFile(Service|Application $resource, bool $isNew = false, int $pull_request_id = 0, bool $is_pr = false)
function parseDockerComposeFile(Service|Application $resource, bool $isNew = false, int $pull_request_id = 0, ?int $preview_id = null)
{
// ray()->clearAll();
if ($resource->getMorphClass() === 'App\Models\Service') {
if ($resource->docker_compose_raw) {
try {
@@ -912,7 +911,7 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
} else if ($type->value() === 'volume') {
if ($topLevelVolumes->has($source->value())) {
$v = $topLevelVolumes->get($source->value());
if (data_get($v, 'driver_opts')) {
if (data_get($v, 'driver_opts.type') === 'cifs') {
return $volume;
}
}
@@ -1283,20 +1282,11 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
$isSameDockerComposeFile = false;
if ($resource->dockerComposePrLocation() === $resource->dockerComposeLocation()) {
$isSameDockerComposeFile = true;
$is_pr = false;
}
if ($is_pr) {
try {
$yaml = Yaml::parse($resource->docker_compose_pr_raw);
} catch (\Exception $e) {
return;
}
} else {
try {
$yaml = Yaml::parse($resource->docker_compose_raw);
} catch (\Exception $e) {
return;
}
try {
$yaml = Yaml::parse($resource->docker_compose_raw);
} catch (\Exception $e) {
return;
}
$server = $resource->destination->server;
$topLevelVolumes = collect(data_get($yaml, 'volumes', []));
@@ -1330,7 +1320,7 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
if ($pull_request_id !== 0) {
$definedNetwork = collect(["{$resource->uuid}-$pull_request_id"]);
}
$services = collect($services)->map(function ($service, $serviceName) use ($topLevelVolumes, $topLevelNetworks, $definedNetwork, $isNew, $generatedServiceFQDNS, $resource, $server, $pull_request_id) {
$services = collect($services)->map(function ($service, $serviceName) use ($topLevelVolumes, $topLevelNetworks, $definedNetwork, $isNew, $generatedServiceFQDNS, $resource, $server, $pull_request_id, $preview_id) {
$serviceVolumes = collect(data_get($service, 'volumes', []));
$servicePorts = collect(data_get($service, 'ports', []));
$serviceNetworks = collect(data_get($service, 'networks', []));
@@ -1380,8 +1370,13 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
$volume = str("$name:$mount");
if ($topLevelVolumes->has($name)) {
$v = $topLevelVolumes->get($name);
if (data_get($v, 'driver_opts')) {
if (data_get($v, 'driver_opts.type') === 'cifs') {
// Do nothing
} else {
if (is_null(data_get($v, 'name'))) {
data_set($v, 'name', $name);
data_set($topLevelVolumes, $name, $v);
}
}
} else {
$topLevelVolumes->put($name, [
@@ -1391,8 +1386,12 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
} else {
if ($topLevelVolumes->has($name->value())) {
$v = $topLevelVolumes->get($name->value());
if (data_get($v, 'driver_opts')) {
if (data_get($v, 'driver_opts.type') === 'cifs') {
// Do nothing
} else {
if (is_null(data_get($v, 'name'))) {
data_set($topLevelVolumes, $name->value(), $v);
}
}
} else {
$topLevelVolumes->put($name->value(), [
@@ -1444,8 +1443,13 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
if (!str($source)->startsWith('/')) {
if ($topLevelVolumes->has($source)) {
$v = $topLevelVolumes->get($source);
if (data_get($v, 'driver_opts')) {
if (data_get($v, 'driver_opts.type') === 'cifs') {
// Do nothing
} else {
if (is_null(data_get($v, 'name'))) {
data_set($v, 'name', $source);
data_set($topLevelVolumes, $source, $v);
}
}
} else {
$topLevelVolumes->put($source, [
@@ -1716,21 +1720,32 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
if ($fqdns) {
$fqdns = str($fqdns)->explode(',');
if ($pull_request_id !== 0) {
$fqdns = $fqdns->map(function ($fqdn) use ($pull_request_id, $resource) {
$preview = ApplicationPreview::findPreviewByApplicationAndPullId($resource->id, $pull_request_id);
$url = Url::fromString($fqdn);
$template = $resource->preview_url_template;
$host = $url->getHost();
$schema = $url->getScheme();
$random = new Cuid2(7);
$preview_fqdn = str_replace('{{random}}', $random, $template);
$preview_fqdn = str_replace('{{domain}}', $host, $preview_fqdn);
$preview_fqdn = str_replace('{{pr_id}}', $pull_request_id, $preview_fqdn);
$preview_fqdn = "$schema://$preview_fqdn";
$preview->fqdn = $preview_fqdn;
$preview->save();
return $preview_fqdn;
});
$preview = $resource->previews()->find($preview_id);
$docker_compose_domains = collect(json_decode(data_get($preview, 'docker_compose_domains')));
if ($docker_compose_domains->count() > 0) {
$found_fqdn = data_get($docker_compose_domains, "$serviceName.domain");
if ($found_fqdn) {
$fqdns = collect($found_fqdn);
} else {
$fqdns = collect([]);
}
} else {
$fqdns = $fqdns->map(function ($fqdn) use ($pull_request_id, $resource) {
$preview = ApplicationPreview::findPreviewByApplicationAndPullId($resource->id, $pull_request_id);
$url = Url::fromString($fqdn);
$template = $resource->preview_url_template;
$host = $url->getHost();
$schema = $url->getScheme();
$random = new Cuid2(7);
$preview_fqdn = str_replace('{{random}}', $random, $template);
$preview_fqdn = str_replace('{{domain}}', $host, $preview_fqdn);
$preview_fqdn = str_replace('{{pr_id}}', $pull_request_id, $preview_fqdn);
$preview_fqdn = "$schema://$preview_fqdn";
$preview->fqdn = $preview_fqdn;
$preview->save();
return $preview_fqdn;
});
}
}
$serviceLabels = $serviceLabels->merge(fqdnLabelsForTraefik(
uuid: $resource->uuid,
@@ -1797,13 +1812,8 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
$resource->docker_compose_raw = Yaml::dump($yaml, 10, 2);
$resource->docker_compose = Yaml::dump($finalServices, 10, 2);
} else {
if ($is_pr) {
$resource->docker_compose_pr_raw = Yaml::dump($yaml, 10, 2);
$resource->docker_compose_pr = Yaml::dump($finalServices, 10, 2);
} else {
$resource->docker_compose_raw = Yaml::dump($yaml, 10, 2);
$resource->docker_compose = Yaml::dump($finalServices, 10, 2);
}
$resource->docker_compose_raw = Yaml::dump($yaml, 10, 2);
$resource->docker_compose = Yaml::dump($finalServices, 10, 2);
}
$resource->save();
return collect($finalServices);

View File

@@ -7,7 +7,7 @@ return [
// The release version of your application
// Example with dynamic git hash: trim(exec('git --git-dir ' . base_path('.git') . ' log --pretty="%h" -n1 HEAD'))
'release' => '4.0.0-beta.294',
'release' => '4.0.0-beta.295',
// When left empty or `null` the Laravel environment will be used
'environment' => config('app.env'),

View File

@@ -1,3 +1,3 @@
<?php
return '4.0.0-beta.294';
return '4.0.0-beta.295';

View File

@@ -0,0 +1,28 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('application_previews', function (Blueprint $table) {
$table->text('docker_compose_domains')->nullable();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('application_previews', function (Blueprint $table) {
$table->dropColumn('docker_compose_domains');
});
}
};

View File

@@ -0,0 +1,28 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('application_previews', function (Blueprint $table) {
$table->string('pull_request_issue_comment_id')->nullable()->change();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('application_previews', function (Blueprint $table) {
$table->integer('pull_request_issue_comment_id')->nullable()->change();
});
}
};

View File

@@ -0,0 +1,32 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('services', function (Blueprint $table) {
$table->string('git_repository')->nullable();
$table->string('git_branch')->nullable();
$table->nullableMorphs('source');
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('services', function (Blueprint $table) {
$table->dropColumn('git_repository');
$table->dropColumn('git_branch');
$table->dropMorphs('source');
});
}
};

View File

@@ -0,0 +1,6 @@
<svg width="520" height="520" viewBox="0 0 520 520" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M422.968 205.942C413.422 191.118 400.042 177.995 383.222 166.926C350.724 145.574 308.024 133.811 262.987 133.811C247.939 133.811 233.115 135.12 218.724 137.71C209.796 129.114 199.357 121.382 188.301 115.272C147.282 94.8216 111.134 102.436 92.8693 109.004C86.8687 111.163 85.0173 118.752 89.4554 123.331C102.336 136.624 123.647 162.896 118.408 186.787C98.0427 207.577 87 232.646 87 258.748C87 285.347 98.0427 310.416 118.408 331.206C123.647 355.097 102.336 381.382 89.4554 394.675C85.0304 399.241 86.8687 406.83 92.8693 408.989C111.134 415.557 147.282 423.185 188.314 402.735C199.37 396.625 209.809 388.892 218.737 380.296C233.128 382.887 247.953 384.195 263 384.195C308.05 384.195 350.751 372.446 383.235 351.093C400.055 340.024 413.435 326.914 422.981 312.077C433.617 295.566 439 277.785 439 259.258C438.987 240.234 433.603 222.467 422.968 205.942ZM261.149 353.383C241.676 353.383 223.11 350.871 206.185 346.331L193.816 358.224C187.093 364.687 179.215 370.536 170.995 375.141C160.11 380.466 149.356 383.384 138.721 384.26C139.325 383.175 139.876 382.076 140.467 380.976C152.862 358.211 156.21 337.748 150.499 319.601C130.225 303.678 118.067 283.293 118.067 261.09C118.067 210.129 182.13 168.81 261.149 168.81C340.167 168.81 404.244 210.129 404.244 261.09C404.244 312.064 340.181 353.383 261.149 353.383Z" fill="#F5455C"/>
<path d="M192.7 239.868C181.053 239.868 171.612 249.236 171.612 260.789C171.612 272.342 181.053 281.71 192.7 281.71C204.346 281.71 213.787 272.342 213.787 260.789C213.787 249.236 204.346 239.868 192.7 239.868Z" fill="#F5455C"/>
<path d="M260.571 239.868C248.925 239.868 239.484 249.236 239.484 260.789C239.484 272.342 248.925 281.71 260.571 281.71C272.218 281.71 281.659 272.342 281.659 260.789C281.659 249.236 272.218 239.868 260.571 239.868Z" fill="#F5455C"/>
<path d="M328.455 239.868C316.808 239.868 307.367 249.236 307.367 260.789C307.367 272.342 316.808 281.71 328.455 281.71C340.101 281.71 349.542 272.342 349.542 260.789C349.542 249.236 340.101 239.868 328.455 239.868Z" fill="#F5455C"/>
</svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@@ -45,24 +45,48 @@
</a>
@endforeach
@endif
@if (data_get($application, 'previews', collect([]))->count() > 0)
@foreach (data_get($application, 'previews') as $preview)
@if (data_get($preview, 'fqdn'))
<a class="dropdown-item" target="_blank"
href="{{ getFqdnWithoutPort(data_get($preview, 'fqdn')) }}">
<svg xmlns="http://www.w3.org/2000/svg" class="w-6 h-6" viewBox="0 0 24 24" stroke-width="1.5"
stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
<path d="M9 15l6 -6" />
<path d="M11 6l.463 -.536a5 5 0 0 1 7.071 7.072l-.534 .464" />
<path
d="M13 18l-.397 .534a5.068 5.068 0 0 1 -7.127 0a4.972 4.972 0 0 1 0 -7.071l.524 -.463" />
</svg>
PR{{ data_get($preview, 'pull_request_id') }} |
{{ data_get($preview, 'fqdn') }}
</a>
@endif
@endforeach
@if (data_get($application, 'previews', collect())->count() > 0)
@if (data_get($application, 'build_pack') === 'dockercompose')
@foreach ($application->previews as $preview)
@foreach (collect(json_decode($preview->docker_compose_domains)) as $fqdn)
@if (data_get($fqdn, 'domain'))
@foreach (explode(',', data_get($fqdn, 'domain')) as $domain)
<a class="dropdown-item" target="_blank" href="{{ getFqdnWithoutPort($domain) }}">
<svg xmlns="http://www.w3.org/2000/svg" class="w-6 h-6" viewBox="0 0 24 24"
stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round"
stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
<path d="M9 15l6 -6" />
<path d="M11 6l.463 -.536a5 5 0 0 1 7.071 7.072l-.534 .464" />
<path
d="M13 18l-.397 .534a5.068 5.068 0 0 1 -7.127 0a4.972 4.972 0 0 1 0 -7.071l.524 -.463" />
</svg>PR{{ data_get($preview, 'pull_request_id') }} |
{{ getFqdnWithoutPort($domain) }}
</a>
@endforeach
@endif
@endforeach
@endforeach
@else
@foreach (data_get($application, 'previews') as $preview)
@if (data_get($preview, 'fqdn'))
<a class="dropdown-item" target="_blank"
href="{{ getFqdnWithoutPort(data_get($preview, 'fqdn')) }}">
<svg xmlns="http://www.w3.org/2000/svg" class="w-6 h-6" viewBox="0 0 24 24"
stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round"
stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
<path d="M9 15l6 -6" />
<path d="M11 6l.463 -.536a5 5 0 0 1 7.071 7.072l-.534 .464" />
<path
d="M13 18l-.397 .534a5.068 5.068 0 0 1 -7.127 0a4.972 4.972 0 0 1 0 -7.071l.524 -.463" />
</svg>
PR{{ data_get($preview, 'pull_request_id') }} |
{{ data_get($preview, 'fqdn') }}
</a>
@endif
@endforeach
@endif
@endif
@if (data_get($application, 'ports_mappings_array'))
@foreach ($application->ports_mappings_array as $port)

View File

@@ -1,4 +1,7 @@
<x-layout>
<x-slot:title>
Destinations | Coolify
</x-slot>
<div class="flex items-start gap-2">
<h1>Destinations</h1>
@if ($servers->count() > 0)

View File

@@ -15,7 +15,7 @@
<link rel="preload" href="https://cdn.fonts.coollabs.io/inter/normal/800.woff2" as="style" />
<link href="https://api.fonts.coollabs.io/css2?family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet">
<meta name="robots" content="noindex">
<title>Coolify</title>
<title>{{ $title ?? 'Coolify' }}</title>
@env('local')
<link rel="icon" href="{{ asset('favicon-dev.png') }}" type="image/x-icon" />
@else

View File

@@ -1,4 +1,7 @@
@php use App\Enums\ProxyTypes; @endphp
<x-slot:title>
Onboarding | Coolify
</x-slot>
<section class="flex flex-col h-full lg:items-center lg:justify-center">
<div
class="flex flex-col items-center justify-center p-10 mx-2 mt-10 bg-white border rounded-lg shadow lg:p-20 dark:bg-transparent dark:border-none max-w-7xl ">

View File

@@ -1,4 +1,7 @@
<div>
<x-slot:title>
Command Center | Coolify
</x-slot>
<h1>Command Center</h1>
<div class="subtitle">Execute commands on your servers without leaving the browser.</div>
@if ($servers->count() > 0)

View File

@@ -1,4 +1,7 @@
<div>
<x-slot:title>
Dashboard | Coolify
</x-slot>
@if (session('error'))
<span x-data x-init="$wire.emit('error', '{{ session('error') }}')" />
@endif

View File

@@ -1,4 +1,7 @@
<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">
@@ -7,7 +10,7 @@
Save
</x-forms.button>
@if ($team->discord_enabled)
<x-forms.button class="dark:text-white normal-case btn btn-xs no-animation btn-primary"
<x-forms.button class="normal-case dark:text-white btn btn-xs no-animation btn-primary"
wire:click="sendTestNotification">
Send Test Notifications
</x-forms.button>

View File

@@ -1,4 +1,7 @@
<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">

View File

@@ -1,4 +1,7 @@
<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">

View File

@@ -1,4 +1,7 @@
<div>
<x-slot:title>
Profile | Coolify
</x-slot>
<h1>Profile</h1>
<div class="subtitle ">Your user profile settings.</div>
<form wire:submit='submit' class="flex flex-col">

View File

@@ -1,4 +1,7 @@
<div>
<x-slot:title>
{{ data_get_str($application, 'name')->limit(10) }} > Configuration | Coolify
</x-slot>
<h1>Configuration</h1>
<livewire:project.shared.configuration-checker :resource="$application" />
<livewire:project.application.heading :application="$application" />

View File

@@ -1,4 +1,7 @@
<div>
<x-slot:title>
{{ data_get_str($application, 'name')->limit(10) }} > Deployments | Coolify
</x-slot>
<h1>Deployments</h1>
<livewire:project.shared.configuration-checker :resource="$application" />
<livewire:project.application.heading :application="$application" />

View File

@@ -1,4 +1,7 @@
<div>
<x-slot:title>
{{ data_get_str($application, 'name')->limit(10) }} > Deployment | Coolify
</x-slot>
<h1 class="py-0">Deployment</h1>
<livewire:project.shared.configuration-checker :resource="$application" />
<livewire:project.application.heading :application="$application" />

View File

@@ -38,7 +38,6 @@
</div>
@endif
@if ($application->build_pack === 'dockercompose')
@if (
!is_null($parsedServices) &&
count($parsedServices) > 0 &&
@@ -266,20 +265,24 @@
<h3 class="pt-8">Pre/Post Deployment Commands</h3>
<div class="flex flex-col gap-2 xl:flex-row">
<x-forms.input x-bind:disabled="initLoadingCompose" id="application.pre_deployment_command"
label="Pre-deployment Command"
helper="An optional script or command to execute in the existing container before the deployment begins." />
<x-forms.input x-bind:disabled="initLoadingCompose" id="application.pre_deployment_command_container"
label="Container Name"
helper="The name of the container to execute within. You can leave it blank if your application only has one container." />
<x-forms.input x-bind:disabled="initLoadingCompose" placeholder="php artisan migrate"
id="application.pre_deployment_command" label="Pre-deployment "
helper="An optional script or command to execute in the existing container before the deployment begins.<br>It is always executed with 'sh -c', so you do not need add it manually." />
@if ($application->build_pack === 'dockercompose')
<x-forms.input x-bind:disabled="initLoadingCompose"
id="application.pre_deployment_command_container" label="Container Name"
helper="The name of the container to execute within. You can leave it blank if your application only has one container." />
@endif
</div>
<div class="flex flex-col gap-2 xl:flex-row">
<x-forms.input x-bind:disabled="initLoadingCompose" placeholder="php artisan migrate"
id="application.post_deployment_command" label="Post-deployment Command"
helper="An optional script or command to execute in the newly built container after the deployment completes." />
<x-forms.input x-bind:disabled="initLoadingCompose" id="application.post_deployment_command_container"
label="Container Name"
helper="The name of the container to execute within. You can leave it blank if your application only has one container." />
id="application.post_deployment_command" label="Post-deployment "
helper="An optional script or command to execute in the newly built container after the deployment completes.<br>It is always executed with 'sh -c', so you do not need add it manually." />
@if ($application->build_pack === 'dockercompose')
<x-forms.input x-bind:disabled="initLoadingCompose"
id="application.post_deployment_command_container" label="Container Name"
helper="The name of the container to execute within. You can leave it blank if your application only has one container." />
@endif
</div>
</div>
</form>

View File

@@ -0,0 +1,7 @@
<form wire:submit="save" class="flex items-end gap-2">
<x-forms.input helper="One domain per preview." label="Domains for {{ str($serviceName)->headline() }}"
id="service.domain"></x-forms.input>
<x-forms.button type="submit">Save</x-forms.button>
<x-forms.button wire:click="generate">Generate
Domain</x-forms.button>
</form>

View File

@@ -42,11 +42,16 @@
<td class="flex flex-col gap-1 md:flex-row">
<x-forms.button
wire:click="add('{{ data_get($pull_request, 'number') }}', '{{ data_get($pull_request, 'html_url') }}')">
Add
Configure
</x-forms.button>
<x-forms.button
wire:click="deploy('{{ data_get($pull_request, 'number') }}', '{{ data_get($pull_request, 'html_url') }}')">
Deploy
<svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5 dark:text-warning"
viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"
fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
<path d="M7 4v16l13 -8z" />
</svg>Deploy
</x-forms.button>
</td>
</tr>
@@ -58,7 +63,7 @@
</div>
</div>
@if ($application->previews->count() > 0)
<div class="pb-4">Previews</div>
<h3 class="py-4">Deployments</h3>
<div class="flex flex-wrap w-full gap-4">
@foreach (data_get($application, 'previews') as $previewName => $preview)
<div class="flex flex-col w-full p-4 border dark:border-coolgray-200">
@@ -81,21 +86,35 @@
<x-external-link />
</a>
</div>
<form wire:submit="save_preview('{{ $preview->id }}')" class="flex items-end gap-2 pt-4">
<x-forms.input label="Domain" helper="One domain per preview."
id="application.previews.{{ $previewName }}.fqdn"></x-forms.input>
<x-forms.button type="submit">Save</x-forms.button>
<x-forms.button wire:click="generate_preview('{{ $preview->id }}')">Generate
Domain</x-forms.button>
</form>
<div class="flex items-center gap-2 pt-6">
<x-forms.button wire:click="deploy({{ data_get($preview, 'pull_request_id') }})">
@if (data_get($preview, 'status') === 'exited')
Deploy
@if ($application->build_pack === 'dockercompose')
<div class="flex flex-col gap-4 pt-4">
@if (collect(json_decode($preview->docker_compose_domains))->count() === 0)
<form wire:submit="save_preview('{{ $preview->id }}')"
class="flex items-end gap-2 pt-4">
<x-forms.input label="Domain" helper="One domain per preview."
id="application.previews.{{ $previewName }}.fqdn"></x-forms.input>
<x-forms.button type="submit">Save</x-forms.button>
<x-forms.button wire:click="generate_preview('{{ $preview->id }}')">Generate
Domain</x-forms.button>
</form>
@else
Redeploy
@foreach (collect(json_decode($preview->docker_compose_domains)) as $serviceName => $service)
<livewire:project.application.previews-compose wire:key="{{ $preview->id }}"
:service="$service" :serviceName="$serviceName" :preview="$preview" />
@endforeach
@endif
</x-forms.button>
</div>
@else
<form wire:submit="save_preview('{{ $preview->id }}')" class="flex items-end gap-2 pt-4">
<x-forms.input label="Domain" helper="One domain per preview."
id="application.previews.{{ $previewName }}.fqdn"></x-forms.input>
<x-forms.button type="submit">Save</x-forms.button>
<x-forms.button wire:click="generate_preview('{{ $preview->id }}')">Generate
Domain</x-forms.button>
</form>
@endif
<div class="flex items-center gap-2 pt-6">
@if (count($parameters) > 0)
<a
href="{{ route('project.application.deployment.index', [...$parameters, 'pull_request_id' => data_get($preview, 'pull_request_id')]) }}">
@@ -111,6 +130,27 @@
</a>
@endif
<div class="flex-1"></div>
<x-forms.button wire:click="deploy({{ data_get($preview, 'pull_request_id') }})">
@if (data_get($preview, 'status') === 'exited')
<svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5 dark:text-warning"
viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none"
stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
<path d="M7 4v16l13 -8z" />
</svg>
Deploy
@else
<svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5 dark:text-orange-400"
viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none"
stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
<path
d="M10.09 4.01l.496 -.495a2 2 0 0 1 2.828 0l7.071 7.07a2 2 0 0 1 0 2.83l-7.07 7.07a2 2 0 0 1 -2.83 0l-7.07 -7.07a2 2 0 0 1 0 -2.83l3.535 -3.535h-3.988">
</path>
<path d="M7.05 11.038v-3.988"></path>
</svg> Redeploy
@endif
</x-forms.button>
@if (data_get($preview, 'status') !== 'exited')
<x-modal-confirmation isErrorButton
action="stop({{ data_get($preview, 'pull_request_id') }})">

View File

@@ -1,4 +1,7 @@
<form>
<x-slot:title>
{{ data_get_str($project, 'name')->limit(10) }} > Clone | Coolify
</x-slot>
<div class="flex flex-col">
<h1>Clone</h1>
<div class="subtitle ">Quickly clone all resources to a new project or environment.</div>

View File

@@ -1,4 +1,4 @@
<div>
<div wire:init='refreshBackupExecutions'>
@isset($backup)
<div class="flex items-center gap-2">
<h3 class="py-4">Executions</h3>

View File

@@ -1,9 +1,12 @@
<div>
<x-slot:title>
{{ data_get_str($database, 'name')->limit(10) }} > Backup | Coolify
</x-slot>
<h1>Backups</h1>
<livewire:project.shared.configuration-checker :resource="$database" />
<livewire:project.database.heading :database="$database" />
<div class="pt-6">
<livewire:project.database.backup-edit :backup="$backup" :s3s="$s3s" :status="data_get($database, 'status')" />
<livewire:project.database.backup-executions :backup="$backup" :executions="$executions" />
<livewire:project.database.backup-executions :backup="$backup" />
</div>
</div>

View File

@@ -1,12 +1,15 @@
<div>
<x-slot:title>
{{ data_get_str($database, 'name')->limit(10) }} > Backups | Coolify
</x-slot>
<h1>Backups</h1>
<livewire:project.shared.configuration-checker :resource="$database" />
<livewire:project.database.heading :database="$database" />
<div class="pt-6">
<div class="flex gap-2 ">
<div class="flex gap-2">
<h2 class="pb-4">Scheduled Backups</h2>
<x-modal-input buttonTitle="+ Add" title="New Scheduled Backup">
<livewire:project.database.create-scheduled-backup :database="$database" :s3s="$s3s" />
<livewire:project.database.create-scheduled-backup :database="$database" />
</x-modal-input>
</div>
<livewire:project.database.scheduled-backups :database="$database" />

View File

@@ -15,8 +15,8 @@
@if ($database->started_at)
<div class="flex gap-2">
<x-forms.input label="Initial Username" id="database.clickhouse_admin_user" placeholder="If empty: clickhouse"
readonly helper="You can only change this in the database." />
<x-forms.input label="Initial Username" id="database.clickhouse_admin_user"
placeholder="If empty: clickhouse" readonly helper="You can only change this in the database." />
<x-forms.input label="Initial Password" id="database.clickhouse_admin_password" type="password" required
readonly helper="You can only change this in the database." />
</div>
@@ -34,9 +34,6 @@
<div class="flex items-end gap-2">
<x-forms.input placeholder="3000:5432" id="database.ports_mappings" label="Ports Mappings"
helper="A comma separated list of ports you would like to map to the host system.<br><span class='inline-block font-bold dark:text-warning'>Example</span>3000:5432,3002:5433" />
<x-forms.input placeholder="5432" disabled="{{ $database->is_public }}" id="database.public_port"
label="Public Port" />
<x-forms.checkbox instantSave id="database.is_public" label="Make it publicly available" />
</div>
<x-forms.input label="Clickhouse URL (internal)"
helper="If you change the user/password/port, this could be different. This is with the default values."
@@ -47,6 +44,23 @@
type="password" readonly wire:model="db_url_public" />
@endif
</div>
<div>
<h3 class="py-2">Proxy</h3>
<div class="flex items-end gap-2">
<x-forms.input placeholder="5432" disabled="{{ data_get($database, 'is_public') }}"
id="database.public_port" label="Public Port" />
<x-slide-over fullScreen>
<x-slot:title>Proxy Logs</x-slot:title>
<x-slot:content>
<livewire:project.shared.get-logs :server="$server" :resource="$database"
container="{{ data_get($database, 'uuid') }}-proxy" lazy />
</x-slot:content>
<x-forms.button disabled="{{ !data_get($database, 'is_public') }}" @click="slideOverOpen=true"
class="w-28">Proxy Logs</x-forms.button>
</x-slide-over>
<x-forms.checkbox instantSave id="database.is_public" label="Make it publicly available" />
</div>
</div>
</form>
<h3 class="pt-4">Advanced</h3>
<div class="flex flex-col">

View File

@@ -1,4 +1,7 @@
<div>
<x-slot:title>
{{ data_get_str($database, 'name')->limit(10) }} > Configuration | Coolify
</x-slot>
<h1>Configuration</h1>
<livewire:project.shared.configuration-checker :resource="$database" />
<livewire:project.database.heading :database="$database" />

View File

@@ -16,9 +16,6 @@
<div class="flex items-end gap-2">
<x-forms.input placeholder="3000:5432" id="database.ports_mappings" label="Ports Mappings"
helper="A comma separated list of ports you would like to map to the host system.<br><span class='inline-block font-bold dark:text-warning'>Example</span>3000:5432,3002:5433" />
<x-forms.input placeholder="5432" disabled="{{ $database->is_public }}" id="database.public_port"
label="Public Port" />
<x-forms.checkbox instantSave id="database.is_public" label="Make it publicly available" />
</div>
<x-forms.input label="Dragonfly URL (internal)"
helper="If you change the user/password/port, this could be different. This is with the default values."
@@ -29,6 +26,23 @@
type="password" readonly wire:model="db_url_public" />
@endif
</div>
<div>
<h3 class="py-2">Proxy</h3>
<div class="flex items-end gap-2">
<x-forms.input placeholder="5432" disabled="{{ data_get($database, 'is_public') }}"
id="database.public_port" label="Public Port" />
<x-slide-over fullScreen>
<x-slot:title>Proxy Logs</x-slot:title>
<x-slot:content>
<livewire:project.shared.get-logs :server="$server" :resource="$database"
container="{{ data_get($database, 'uuid') }}-proxy" lazy />
</x-slot:content>
<x-forms.button disabled="{{ !data_get($database, 'is_public') }}" @click="slideOverOpen=true"
class="w-28">Proxy Logs</x-forms.button>
</x-slide-over>
<x-forms.checkbox instantSave id="database.is_public" label="Make it publicly available" />
</div>
</div>
{{-- <x-forms.textarea
helper="<a target='_blank' class='underline dark:text-white' href='https://raw.githubusercontent.com/Snapchat/KeyDB/unstable/keydb.conf'>KeyDB Default Configuration</a>"
label="Custom Dragonfly Configuration" rows="10" id="database.keydb_conf" /> --}}

View File

@@ -17,9 +17,6 @@
<div class="flex items-end gap-2">
<x-forms.input placeholder="3000:5432" id="database.ports_mappings" label="Ports Mappings"
helper="A comma separated list of ports you would like to map to the host system.<br><span class='inline-block font-bold dark:text-warning'>Example</span>3000:5432,3002:5433" />
<x-forms.input placeholder="5432" disabled="{{ $database->is_public }}" id="database.public_port"
label="Public Port" />
<x-forms.checkbox instantSave id="database.is_public" label="Make it publicly available" />
</div>
<x-forms.input label="KeyDB URL (internal)"
helper="If you change the user/password/port, this could be different. This is with the default values."
@@ -30,6 +27,23 @@
type="password" readonly wire:model="db_url_public" />
@endif
</div>
<div>
<h3 class="py-2">Proxy</h3>
<div class="flex items-end gap-2">
<x-forms.input placeholder="5432" disabled="{{ data_get($database, 'is_public') }}"
id="database.public_port" label="Public Port" />
<x-slide-over fullScreen>
<x-slot:title>Proxy Logs</x-slot:title>
<x-slot:content>
<livewire:project.shared.get-logs :server="$server" :resource="$database"
container="{{ data_get($database, 'uuid') }}-proxy" lazy />
</x-slot:content>
<x-forms.button disabled="{{ !data_get($database, 'is_public') }}" @click="slideOverOpen=true"
class="w-28">Proxy Logs</x-forms.button>
</x-slide-over>
<x-forms.checkbox instantSave id="database.is_public" label="Make it publicly available" />
</div>
</div>
<x-forms.textarea
helper="<a target='_blank' class='underline dark:text-white' href='https://raw.githubusercontent.com/Snapchat/KeyDB/unstable/keydb.conf'>KeyDB Default Configuration</a>"
label="Custom KeyDB Configuration" rows="10" id="database.keydb_conf" />

View File

@@ -12,23 +12,23 @@
<x-forms.input label="Image" id="database.image" required
helper="For all available images, check here:<br><br><a target='_blank' href='https://hub.docker.com/_/mariadb'>https://hub.docker.com/_/mariadb</a>" />
</div>
<div class="pt-2 dark:text-warning">If you change the values in the database, please sync it here, otherwise
automations (like backups) won't work.
</div>
@if ($database->started_at)
<div class="flex gap-2">
<x-forms.input label="Root Password" id="database.mariadb_root_password" type="password" readonly
helper="You can only change this in the database." />
<x-forms.input label="Normal User" id="database.mariadb_user" required readonly
helper="You can only change this in the database." />
<div class="flex flex-col gap-2">
<x-forms.input label="Root Password" id="database.mariadb_root_password" type="password" required
helper="If you change this in the database, please sync it here, otherwise automations (like backups) won't work." />
<x-forms.input label="Normal User" id="database.mariadb_user" required
helper="If you change this in the database, please sync it here, otherwise automations (like backups) won't work." />
<x-forms.input label="Normal User Password" id="database.mariadb_password" type="password" required
readonly helper="You can only change this in the database." />
helper="If you change this in the database, please sync it here, otherwise automations (like backups) won't work." />
<x-forms.input label="Initial Database" id="database.mariadb_database"
placeholder="If empty, it will be the same as Username." readonly
helper="You can only change this in the database." />
</div>
@else
<div class="pt-8 dark:text-warning">Please verify these values. You can only modify them before the initial
start. After that, you need to modify it in the database.
</div>
<div class="flex gap-2 pb-8">
<div class="flex flex-col gap-2 pb-2">
<x-forms.input label="Root Password" id="database.mariadb_root_password" type="password"
helper="You can only change this in the database." />
<x-forms.input label="Normal User" id="database.mariadb_user" required
@@ -45,9 +45,6 @@
<div class="flex items-end gap-2">
<x-forms.input placeholder="3000:5432" id="database.ports_mappings" label="Ports Mappings"
helper="A comma separated list of ports you would like to map to the host system.<br><span class='inline-block font-bold dark:text-warning'>Example</span>3000:5432,3002:5433" />
<x-forms.input placeholder="5432" disabled="{{ $database->is_public }}" id="database.public_port"
label="Public Port" />
<x-forms.checkbox instantSave id="database.is_public" label="Make it publicly available" />
</div>
<x-forms.input label="MariaDB URL (internal)"
helper="If you change the user/password/port, this could be different. This is with the default values."
@@ -58,6 +55,23 @@
type="password" readonly wire:model="db_url_public" />
@endif
</div>
<div>
<h3 class="py-2">Proxy</h3>
<div class="flex items-end gap-2">
<x-forms.input placeholder="5432" disabled="{{ data_get($database, 'is_public') }}"
id="database.public_port" label="Public Port" />
<x-slide-over fullScreen>
<x-slot:title>Proxy Logs</x-slot:title>
<x-slot:content>
<livewire:project.shared.get-logs :server="$server" :resource="$database"
container="{{ data_get($database, 'uuid') }}-proxy" lazy />
</x-slot:content>
<x-forms.button disabled="{{ !data_get($database, 'is_public') }}" @click="slideOverOpen=true"
class="w-28">Proxy Logs</x-forms.button>
</x-slide-over>
<x-forms.checkbox instantSave id="database.is_public" label="Make it publicly available" />
</div>
</div>
<x-forms.textarea label="Custom MariaDB Configuration" rows="10" id="database.mariadb_conf" />
<h3 class="pt-4">Advanced</h3>
<div class="flex flex-col">

View File

@@ -12,21 +12,23 @@
<x-forms.input label="Image" id="database.image" required
helper="For all available images, check here:<br><br><a target='_blank' href='https://hub.docker.com/_/mongo'>https://hub.docker.com/_/mongo</a>" />
</div>
<div class="pt-2 dark:text-warning">If you change the values in the database, please sync it here, otherwise
automations (like backups) won't work.
</div>
@if ($database->started_at)
<div class="flex gap-2">
<div class="flex flex-col gap-2">
<x-forms.input label="Initial Username" id="database.mongo_initdb_root_username"
placeholder="If empty: postgres" readonly helper="You can only change this in the database." />
placeholder="If empty: postgres"
helper="If you change this in the database, please sync it here, otherwise automations (like backups) won't work." />
<x-forms.input label="Initial Password" id="database.mongo_initdb_root_password" type="password"
required readonly helper="You can only change this in the database." />
required
helper="If you change this in the database, please sync it here, otherwise automations (like backups) won't work." />
<x-forms.input label="Initial Database" id="database.mongo_initdb_database"
placeholder="If empty, it will be the same as Username." readonly
helper="You can only change this in the database." />
</div>
@else
<div class="pt-8 dark:text-warning">Please verify these values. You can only modify them before the initial
start. After that, you need to modify it in the database.
</div>
<div class="flex gap-2 pb-8">
<div class="flex flex-col gap-2 pb-2">
<x-forms.input required label="Username" id="database.mongo_initdb_root_username"
placeholder="If empty: postgres" />
<x-forms.input label="Password" id="database.mongo_initdb_root_password" type="password" required />
@@ -39,9 +41,6 @@
<div class="flex items-end gap-2">
<x-forms.input placeholder="3000:5432" id="database.ports_mappings" label="Ports Mappings"
helper="A comma separated list of ports you would like to map to the host system.<br><span class='inline-block font-bold dark:text-warning'>Example</span>3000:5432,3002:5433" />
<x-forms.input placeholder="5432" disabled="{{ $database->is_public }}" id="database.public_port"
label="Public Port" />
<x-forms.checkbox instantSave id="database.is_public" label="Make it publicly available" />
</div>
<x-forms.input label="Mongo URL (internal)"
helper="If you change the user/password/port, this could be different. This is with the default values."
@@ -52,6 +51,23 @@
type="password" readonly wire:model="db_url_public" />
@endif
</div>
<div>
<h3 class="py-2">Proxy</h3>
<div class="flex items-end gap-2">
<x-forms.input placeholder="5432" disabled="{{ data_get($database, 'is_public') }}"
id="database.public_port" label="Public Port" />
<x-slide-over fullScreen>
<x-slot:title>Proxy Logs</x-slot:title>
<x-slot:content>
<livewire:project.shared.get-logs :server="$server" :resource="$database"
container="{{ data_get($database, 'uuid') }}-proxy" lazy />
</x-slot:content>
<x-forms.button disabled="{{ !data_get($database, 'is_public') }}" @click="slideOverOpen=true"
class="w-28">Proxy Logs</x-forms.button>
</x-slide-over>
<x-forms.checkbox instantSave id="database.is_public" label="Make it publicly available" />
</div>
</div>
<x-forms.textarea label="Custom MongoDB Configuration" rows="10" id="database.mongo_conf" />
<h3 class="pt-4">Advanced</h3>
<div class="flex flex-col">

View File

@@ -12,23 +12,23 @@
<x-forms.input label="Image" id="database.image" required
helper="For all available images, check here:<br><br><a target='_blank' href='https://hub.docker.com/_/mysql'>https://hub.docker.com/_/mysql</a>" />
</div>
<div class="pt-2 dark:text-warning">If you change the values in the database, please sync it here, otherwise
automations (like backups) won't work.
</div>
@if ($database->started_at)
<div class="flex gap-2">
<x-forms.input label="Root Password" id="database.mysql_root_password" type="password" readonly
helper="You can only change this in the database." />
<x-forms.input label="Normal User" id="database.mysql_user" required readonly
helper="You can only change this in the database." />
<div class="flex flex-col gap-2">
<x-forms.input label="Root Password" id="database.mysql_root_password" type="password" required
helper="If you change this in the database, please sync it here, otherwise automations (like backups) won't work." />
<x-forms.input label="Normal User" id="database.mysql_user" required
helper="If you change this in the database, please sync it here, otherwise automations (like backups) won't work." />
<x-forms.input label="Normal User Password" id="database.mysql_password" type="password" required
readonly helper="You can only change this in the database." />
helper="If you change this in the database, please sync it here, otherwise automations (like backups) won't work." />
<x-forms.input label="Initial Database" id="database.mysql_database"
placeholder="If empty, it will be the same as Username." readonly
helper="You can only change this in the database." />
</div>
@else
<div class="pt-8 dark:text-warning">Please verify these values. You can only modify them before the initial
start. After that, you need to modify it in the database.
</div>
<div class="flex gap-2 pb-8">
<div class="flex flex-col gap-4 pb-2">
<x-forms.input label="Root Password" id="database.mysql_root_password" type="password"
helper="You can only change this in the database." />
<x-forms.input label="Normal User" id="database.mysql_user" required
@@ -45,9 +45,6 @@
<div class="flex items-end gap-2">
<x-forms.input placeholder="3000:5432" id="database.ports_mappings" label="Ports Mappings"
helper="A comma separated list of ports you would like to map to the host system.<br><span class='inline-block font-bold dark:text-warning'>Example</span>3000:5432,3002:5433" />
<x-forms.input placeholder="5432" disabled="{{ $database->is_public }}" id="database.public_port"
label="Public Port" />
<x-forms.checkbox instantSave id="database.is_public" label="Make it publicly available" />
</div>
<x-forms.input label="MySQL URL (internal)"
helper="If you change the user/password/port, this could be different. This is with the default values."
@@ -58,6 +55,23 @@
type="password" readonly wire:model="db_url_public" />
@endif
</div>
<div>
<h3 class="py-2">Proxy</h3>
<div class="flex items-end gap-2">
<x-forms.input placeholder="5432" disabled="{{ data_get($database, 'is_public') }}"
id="database.public_port" label="Public Port" />
<x-slide-over fullScreen>
<x-slot:title>Proxy Logs</x-slot:title>
<x-slot:content>
<livewire:project.shared.get-logs :server="$server" :resource="$database"
container="{{ data_get($database, 'uuid') }}-proxy" lazy />
</x-slot:content>
<x-forms.button disabled="{{ !data_get($database, 'is_public') }}" @click="slideOverOpen=true"
class="w-28">Proxy Logs</x-forms.button>
</x-slide-over>
<x-forms.checkbox instantSave id="database.is_public" label="Make it publicly available" />
</div>
</div>
<x-forms.textarea label="Custom Mysql Configuration" rows="10" id="database.mysql_conf" />
<h3 class="pt-4">Advanced</h3>
<div class="flex flex-col">

View File

@@ -20,31 +20,30 @@
Save
</x-forms.button>
</div>
<div class="flex gap-2 flex-wrap sm:flex-nowrap">
<div class="flex flex-wrap gap-2 sm:flex-nowrap">
<x-forms.input label="Name" id="database.name" />
<x-forms.input label="Description" id="database.description" />
<x-forms.input label="Image" id="database.image" required
helper="For all available images, check here:<br><br><a target='_blank' href='https://hub.docker.com/_/postgres'>https://hub.docker.com/_/postgres</a>" />
</div>
<div class="pt-2 dark:text-warning">If you change the values in the database, please sync it here, otherwise
automations (like backups) won't work.
</div>
@if ($database->started_at)
<div class="flex gap-2 flex-wrap sm:flex-nowrap">
<x-forms.input label="Initial Username" id="database.postgres_user" placeholder="If empty: postgres"
readonly helper="You can only change this in the database." />
<x-forms.input label="Initial Password" id="database.postgres_password" type="password" required
readonly helper="You can only change this in the database." />
<div class="flex flex-col gap-2">
<x-forms.input label="Username" id="database.postgres_user" placeholder="If empty: postgres"
helper="If you change this in the database, please sync it here, otherwise automations (like backups) won't work." />
<x-forms.input label="Password" id="database.postgres_password" type="password" required
helper="If you change this in the database, please sync it here, otherwise automations (like backups) won't work." />
<x-forms.input label="Initial Database" id="database.postgres_db"
placeholder="If empty, it will be the same as Username." readonly
helper="You can only change this in the database." />
</div>
@else
<div class="pt-8 dark:text-warning">Please verify these values. You can only modify them before the initial
start. After that, you need to modify it in the database.
</div>
<div class="flex gap-2 pb-8">
<div class="flex flex-col gap-2 pb-2">
<x-forms.input label="Username" id="database.postgres_user" placeholder="If empty: postgres" />
<x-forms.input label="Password" id="database.postgres_password" type="password" required />
<x-forms.input label="Database" id="database.postgres_db"
<x-forms.input label="Initial Database" id="database.postgres_db"
placeholder="If empty, it will be the same as Username." />
</div>
@endif
@@ -59,10 +58,8 @@
<div class="flex items-end gap-2">
<x-forms.input placeholder="3000:5432" id="database.ports_mappings" label="Ports Mappings"
helper="A comma separated list of ports you would like to map to the host system.<br><span class='inline-block font-bold dark:text-warning'>Example</span>3000:5432,3002:5433" />
<x-forms.input placeholder="5432" disabled="{{ $database->is_public }}" id="database.public_port"
label="Public Port" />
<x-forms.checkbox instantSave id="database.is_public" label="Make it publicly available" />
</div>
<x-forms.input label="Postgres URL (internal)"
helper="If you change the user/password/port, this could be different. This is with the default values."
type="password" readonly wire:model="db_url" />
@@ -72,6 +69,23 @@
type="password" readonly wire:model="db_url_public" />
@endif
</div>
<div>
<h3 class="py-2">Proxy</h3>
<div class="flex items-end gap-2">
<x-forms.input placeholder="5432" disabled="{{ data_get($database, 'is_public') }}"
id="database.public_port" label="Public Port" />
<x-slide-over fullScreen>
<x-slot:title>Proxy Logs</x-slot:title>
<x-slot:content>
<livewire:project.shared.get-logs :server="$server" :resource="$database"
container="{{ data_get($database, 'uuid') }}-proxy" lazy />
</x-slot:content>
<x-forms.button disabled="{{ !data_get($database, 'is_public') }}" @click="slideOverOpen=true"
class="w-28">Proxy Logs</x-forms.button>
</x-slide-over>
<x-forms.checkbox instantSave id="database.is_public" label="Make it publicly available" />
</div>
</div>
<x-forms.textarea label="Custom PostgreSQL Configuration" rows="10" id="database.postgres_conf" />
</form>
<h3 class="pt-4">Advanced</h3>

View File

@@ -17,9 +17,6 @@
<div class="flex items-end gap-2">
<x-forms.input placeholder="3000:5432" id="database.ports_mappings" label="Ports Mappings"
helper="A comma separated list of ports you would like to map to the host system.<br><span class='inline-block font-bold dark:text-warning'>Example</span>3000:5432,3002:5433" />
<x-forms.input placeholder="5432" disabled="{{ $database->is_public }}" id="database.public_port"
label="Public Port" />
<x-forms.checkbox instantSave id="database.is_public" label="Make it publicly available" />
</div>
<x-forms.input label="Redis URL (internal)"
helper="If you change the user/password/port, this could be different. This is with the default values."
@@ -30,8 +27,25 @@
type="password" readonly wire:model="db_url_public" />
@endif
</div>
<div>
<h3 class="py-2">Proxy</h3>
<div class="flex items-end gap-2">
<x-forms.input placeholder="5432" disabled="{{ data_get($database, 'is_public') }}"
id="database.public_port" label="Public Port" />
<x-slide-over fullScreen>
<x-slot:title>Proxy Logs</x-slot:title>
<x-slot:content>
<livewire:project.shared.get-logs :server="$server" :resource="$database"
container="{{ data_get($database, 'uuid') }}-proxy" lazy />
</x-slot:content>
<x-forms.button disabled="{{ !data_get($database, 'is_public') }}" @click="slideOverOpen=true"
class="w-28">Proxy Logs</x-forms.button>
</x-slide-over>
<x-forms.checkbox instantSave id="database.is_public" label="Make it publicly available" />
</div>
</div>
<x-forms.textarea
helper="<a target='_blank' class='dark:text-white underline' href='https://raw.githubusercontent.com/redis/redis/7.2/redis.conf'>Redis Default Configuration</a>"
helper="<a target='_blank' class='underline dark:text-white' href='https://raw.githubusercontent.com/redis/redis/7.2/redis.conf'>Redis Default Configuration</a>"
label="Custom Redis Configuration" rows="10" id="database.redis_conf" />
<h3 class="pt-4">Advanced</h3>
<div class="flex flex-col">

View File

@@ -1,5 +1,5 @@
<div>
<div class="flex flex-wrap gap-2">
<div class="flex flex-col gap-2">
@forelse($database->scheduledBackups as $backup)
@if ($type == 'database')
<a class="box"
@@ -32,8 +32,7 @@
<livewire:project.database.backup-edit wire:key="{{ $selectedBackup->id }}" :backup="$selectedBackup"
:s3s="$s3s" :status="data_get($database, 'status')" />
<h3 class="py-4">Executions</h3>
<livewire:project.database.backup-executions wire:keykey="{{ $selectedBackup->id }}" :backup="$selectedBackup"
:executions="$selectedBackup->executions" />
<livewire:project.database.backup-executions wire:key="{{ $selectedBackup->id }}" :backup="$selectedBackup" />
</div>
@endif
</div>

View File

@@ -1,4 +1,7 @@
<div>
<x-slot:title>
{{ data_get_str($project, 'name')->limit(10) }} > Edit | Coolify
</x-slot>
<form wire:submit='submit' class="flex flex-col pb-10">
<div class="flex gap-2">
<h1>Project: {{ data_get($project, 'name') }}</h1>

View File

@@ -1,4 +1,7 @@
<div>
<x-slot:title>
{{ data_get_str($project, 'name')->limit(10) }} > Edit | Coolify
</x-slot>
<form wire:submit='submit' class="flex flex-col">
<div class="flex items-end gap-2">
<h1>Environment: {{ data_get($environment, 'name') }}</h1>
@@ -6,7 +9,7 @@
<livewire:project.delete-environment :disabled="!$environment->isEmpty()" :environment_id="$environment->id" />
</div>
<nav class="flex pt-2 pb-10">
<ol class="flex items-center flex-wrap gap-y-1">
<ol class="flex flex-wrap items-center gap-y-1">
<li class="inline-flex items-center">
<div class="flex items-center">
<a class="text-xs truncate lg:text-sm"

View File

@@ -1,4 +1,7 @@
<div>
<x-slot:title>
Projects | Coolify
</x-slot>
<div class="flex gap-2">
<h1>Projects</h1>
<x-modal-input buttonTitle="+ Add" title="New Project">

View File

@@ -5,7 +5,8 @@
<div class="flex flex-col gap-2">
<div class="flex flex-col gap-2">
<div class="flex items-end gap-2">
<x-forms.input required id="repository_url" label="Repository URL (https://)" helper="{!! __('repository.url') !!}" />
<x-forms.input required id="repository_url" label="Repository URL (https://)"
helper="{!! __('repository.url') !!}" />
<x-forms.button type="submit">
Check repository
</x-forms.button>
@@ -58,6 +59,10 @@
helper="If your application is a static site or the final build assets should be served as a static site, enable this." />
</div>
@endif
@if ($build_pack === 'dockercompose' && isDev())
<div class="dark:text-warning">If you choose Docker Compose based deployments, you cannot change it afterwards.</div>
<x-forms.checkbox instantSave label="New Compose Services (only in dev mode)" id="new_compose_services"></x-forms.checkbox>
@endif
</div>
<x-forms.button wire:click.prevent='submit'>
Continue

View File

@@ -1,4 +1,7 @@
<div>
<x-slot:title>
{{ data_get_str($project, 'name')->limit(10) }} > New | Coolify
</x-slot>
@if ($type === 'public')
<livewire:project.new.public-git-repository :type="$type" />
@elseif ($type === 'private-gh-app')

View File

@@ -1,4 +1,7 @@
<div>
<x-slot:title>
{{ data_get_str($project, 'name')->limit(10) }} > Resources | Coolify
</x-slot>
<div class="flex flex-col">
<div class="flex items-center gap-2">
<h1>Resources</h1>

View File

@@ -1,4 +1,7 @@
<div x-data="{ activeTab: window.location.hash ? window.location.hash.substring(1) : 'service-stack' }" x-init="$wire.check_status" wire:poll.5000ms="check_status">
<x-slot:title>
{{ data_get_str($service, 'name')->limit(10) }} > Configuration | Coolify
</x-slot>
<livewire:project.service.navbar :service="$service" :parameters="$parameters" :query="$query" />
<div class="flex flex-col h-full gap-8 pt-6 sm:flex-row">
<div class="flex flex-col items-start gap-2 min-w-fit">

View File

@@ -19,12 +19,18 @@
</div>
<div class="w-full">
@isset($serviceApplication)
<x-slot:title>
{{ data_get_str($service, 'name')->limit(10) }} >
{{ data_get_str($serviceApplication, 'name')->limit(10) }} | Coolify
</x-slot>
<div x-cloak x-show="activeTab === 'general'" class="h-full">
<livewire:project.service.service-application-view :application="$serviceApplication" />
</div>
@endisset
@isset($serviceDatabase)
<x-slot:title>
{{ data_get_str($service, 'name')->limit(10) }} > {{ data_get_str($serviceDatabase, 'name')->limit(10) }} | Coolify
</x-slot>
<div x-cloak x-show="activeTab === 'general'" class="h-full">
<livewire:project.service.database :database="$serviceDatabase" />
</div>

View File

@@ -63,9 +63,11 @@
@else
<x-forms.checkbox instantSave id="env.is_build_time" label="Build Variable?" />
<x-forms.checkbox instantSave id="env.is_multiline" label="Is Multiline?" />
<x-forms.checkbox instantSave id="env.is_literal"
helper="This means that when you use $VARIABLES in a value, it should be interpreted as the actual characters '$VARIABLES' and not as the value of a variable named VARIABLE.<br><br>Useful if you have $ sign in your value and there are some characters after it, but you would not like to interpolate it form another value. In this case, you should set this to true."
label="Is Literal?" />
@if (!data_get($env, 'is_multiline'))
<x-forms.checkbox instantSave id="env.is_literal"
helper="This means that when you use $VARIABLES in a value, it should be interpreted as the actual characters '$VARIABLES' and not as the value of a variable named VARIABLE.<br><br>Useful if you have $ sign in your value and there are some characters after it, but you would not like to interpolate it form another value. In this case, you should set this to true."
label="Is Literal?" />
@endif
@endif
@endif
@endif

View File

@@ -1,4 +1,7 @@
<div>
<x-slot:title>
{{ data_get_str($resource, 'name')->limit(10) }} > Commands | Coolify
</x-slot>
<livewire:project.shared.configuration-checker :resource="$resource" />
@if ($type === 'application')
<h1>Execute Command</h1>

View File

@@ -34,7 +34,7 @@
}
}">
<div class="flex items-center gap-2 ">
@if ($resource?->type() === 'application')
@if ($resource?->type() === 'application' || str($resource?->type())->startsWith('standalone'))
<h4>{{ $container }}</h4>
@else
<h3>{{ str($container)->beforeLast('-')->headline() }}</h3>

View File

@@ -1,4 +1,7 @@
<div>
<x-slot:title>
{{ data_get_str($resource, 'name')->limit(10) }} > Logs | Coolify
</x-slot>
<livewire:project.shared.configuration-checker :resource="$resource" />
@if ($type === 'application')
<h1>Logs</h1>

View File

@@ -1,4 +1,7 @@
<div>
<x-slot:title>
{{ data_get_str($resource, 'name')->limit(10) }} > Scheduled Tasks | Coolify
</x-slot>
@if ($type === 'application')
<h1>Scheduled Task</h1>
<livewire:project.application.heading :application="$resource" />

View File

@@ -1,4 +1,7 @@
<div>
<x-slot:title>
{{ data_get_str($project, 'name')->limit(10) }} > Environments | Coolify
</x-slot>
<div class="flex items-center gap-2">
<h1>Environments</h1>
<x-modal-input buttonTitle="+ Add" title="New Environment">

View File

@@ -1,4 +1,7 @@
<div>
<x-slot:title>
API Tokens | Coolify
</x-slot>
<x-security.navbar />
<div class="flex gap-2">
<h2 class="pb-4">API Tokens</h2>

View File

@@ -1,4 +1,7 @@
<div x-init="$wire.loadPublicKey()">
<x-slot:title>
Private Key | Coolify
</x-slot>
<x-security.navbar />
<div x-data="{ showPrivateKey: false }">
<form class="flex flex-col gap-2" wire:submit='changePrivateKey'>

View File

@@ -1,4 +1,7 @@
<div>
<x-slot:title>
{{ data_get_str($server, 'name')->limit(10) }} > Server Destinations | Coolify
</x-slot>
<x-server.navbar :server="$server" :parameters="$parameters" />
<livewire:destination.show :server="$server" />
</div>

View File

@@ -1,4 +1,7 @@
<div>
<x-slot:title>
Servers | Coolify
</x-slot>
<div class="flex items-start gap-2">
<h1>Servers</h1>
<x-modal-input buttonTitle="+ Add" title="New Server" :closeOutside="false">

View File

@@ -1,4 +1,7 @@
<div>
<x-slot:title>
{{ data_get_str($server, 'name')->limit(10) }} > Server LogDrains | Coolify
</x-slot>
<x-server.navbar :server="$server" :parameters="$parameters" />
@if ($server->isFunctional())
<h2>Log Drains</h2>

View File

@@ -1,4 +1,7 @@
<div>
<x-slot:title>
Server Connection | Coolify
</x-slot>
<x-server.navbar :server="$server" :parameters="$parameters" />
<livewire:server.show-private-key :server="$server" :privateKeys="$privateKeys" />
</div>

View File

@@ -1,4 +1,7 @@
<div>
<x-slot:title>
Proxy Dynamic Configuration | Coolify
</x-slot>
<x-server.navbar :server="$server" :parameters="$parameters" />
<div class="flex gap-2">
<x-server.sidebar :server="$server" :parameters="$parameters" />

View File

@@ -1,4 +1,7 @@
<div>
<x-slot:title>
Proxy Logs | Coolify
</x-slot>
<x-server.navbar :server="$server" :parameters="$parameters" />
<div class="flex gap-2">
<x-server.sidebar :server="$server" :parameters="$parameters" />

View File

@@ -1,4 +1,7 @@
<div>
<x-slot:title>
Proxy Configuration | Coolify
</x-slot>
<x-server.navbar :server="$server" :parameters="$parameters" />
@if ($server->isFunctional())
<div class="flex gap-2">

View File

@@ -1,4 +1,7 @@
<div>
<x-slot:title>
{{ data_get_str($server, 'name')->limit(10) }} > Server Resources | Coolify
</x-slot>
<x-server.navbar :server="$server" :parameters="$parameters" />
<div x-data="{ activeTab: window.location.hash ? window.location.hash.substring(1) : 'managed' }" class="flex flex-col h-full gap-8 md:flex-row">
<div class="flex flex-row gap-4 md:flex-col">

View File

@@ -1,4 +1,7 @@
<div>
<x-slot:title>
{{ data_get_str($server, 'name')->limit(10) }} > Server Configurations | Coolify
</x-slot>
<x-server.navbar :server="$server" :parameters="$parameters" />
<livewire:server.form :server="$server" />
<livewire:server.delete :server="$server" />

View File

@@ -30,7 +30,7 @@
@endif
</div>
<div class="py-4">
<livewire:project.database.backup-executions :backup="$backup" :executions="$executions" />
<livewire:project.database.backup-executions :backup="$backup" />
</div>
</div>
</div>

View File

@@ -1,4 +1,7 @@
<div>
<x-slot:title>
Settings | Coolify
</x-slot>
<x-settings.navbar />
<div x-data="{ activeTab: window.location.hash ? window.location.hash.substring(1) : 'general' }" class="flex flex-col h-full gap-8 pt-1 sm:flex-row">
<div class="flex gap-6 overflow-x-scroll sm:gap-2 sm:overflow-x-hidden scrollbar sm:flex-col whitespace-nowrap">

View File

@@ -1,4 +1,7 @@
<div>
<x-slot:title>
Environment Variables | Coolify
</x-slot>
<div class="flex gap-2">
<h1>Environments</h1>
</div>

View File

@@ -1,4 +1,7 @@
<div>
<x-slot:title>
Environment Variable | Coolify
</x-slot>
<div class="flex gap-2">
<h1>Shared Variables for {{ $project->name }}/{{ $environment->name }}</h1>
<x-modal-input buttonTitle="+ Add" title="New Shared Variable">

View File

@@ -1,4 +1,7 @@
<div>
<x-slot:title>
Shared Variables | Coolify
</x-slot>
<div class="flex items-start gap-2">
<h1>Shared Variables</h1>
</div>

Some files were not shown because too many files have changed in this diff Show More