Compare commits

...

11 commits

Author SHA1 Message Date
Abdulrahman
e32b91b3f4
Merge dd2fe5a3ac into 11007771f0 2026-03-05 19:51:34 +01:00
Andras Bacsai
11007771f0
Fix/wrong destinations api (#8646)
Some checks failed
Staging Build / build-push (aarch64, linux/aarch64, ubuntu-24.04-arm) (push) Has been cancelled
Staging Build / build-push (amd64, linux/amd64, ubuntu-24.04) (push) Has been cancelled
Staging Build / merge-manifest (push) Has been cancelled
2026-03-05 16:32:09 +01:00
Andras Bacsai
db52a20ed8
chore(templates): update n8n templates to 2.10.2 (#8679) 2026-03-05 16:23:47 +01:00
Andras Bacsai
8dfb393de0
chore(ui): add container labels header (#8752)
Some checks are pending
Staging Build / build-push (aarch64, linux/aarch64, ubuntu-24.04-arm) (push) Waiting to run
Staging Build / build-push (amd64, linux/amd64, ubuntu-24.04) (push) Waiting to run
Staging Build / merge-manifest (push) Blocked by required conditions
2026-03-05 14:19:00 +01:00
Cinzya
80be2628d0 chore(ui): add labels header 2026-03-03 20:57:03 +01:00
Jason Trudeau
92cf88070c
Add files via upload
Uploaded:

/templates/compose/n8n-with-postgres-and-worker.yaml
/templates/compose/n8n.yaml
/templates/compose/n8n-with-postgresql.yaml
2026-02-27 22:41:52 -05:00
W8jonas
7c5a6bc96c Fix wrong destination issue on create_service 2026-02-26 23:15:18 -03:00
W8jonas
bf6b3b8c71 Fix wrong destination issue on create_application 2026-02-26 23:15:04 -03:00
TheGenio
dd2fe5a3ac feat: enhance custom container name handling with validation and UI updates 2026-02-10 00:06:22 +03:00
TheGenio
a5599da836 feat: add custom container name prefix option 2026-02-02 01:02:59 +03:00
TheGenio
596f8bcf97 feat: add custom container name prefix option 2026-02-02 00:49:22 +03:00
10 changed files with 145 additions and 13 deletions

View file

@ -1095,6 +1095,17 @@ class ApplicationsController extends Controller
return response()->json(['message' => 'Server has multiple destinations and you do not set destination_uuid.'], 400);
}
$destination = $destinations->first();
if ($destinations->count() > 1 && $request->has('destination_uuid')) {
$destination = $destinations->where('uuid', $request->destination_uuid)->first();
if (! $destination) {
return response()->json([
'message' => 'Validation failed.',
'errors' => [
'destination_uuid' => 'Provided destination_uuid does not belong to the specified server.',
],
], 422);
}
}
if ($type === 'public') {
$validationRules = [
'git_repository' => ['string', 'required', new ValidGitRepositoryUrl],

View file

@ -377,6 +377,17 @@ class ServicesController extends Controller
return response()->json(['message' => 'Server has multiple destinations and you do not set destination_uuid.'], 400);
}
$destination = $destinations->first();
if ($destinations->count() > 1 && $request->has('destination_uuid')) {
$destination = $destinations->where('uuid', $request->destination_uuid)->first();
if (! $destination) {
return response()->json([
'message' => 'Validation failed.',
'errors' => [
'destination_uuid' => 'Provided destination_uuid does not belong to the specified server.',
],
], 422);
}
}
$services = get_service_templates();
$serviceKeys = $services->keys();
if ($serviceKeys->contains($request->type)) {
@ -543,6 +554,17 @@ class ServicesController extends Controller
return response()->json(['message' => 'Server has multiple destinations and you do not set destination_uuid.'], 400);
}
$destination = $destinations->first();
if ($destinations->count() > 1 && $request->has('destination_uuid')) {
$destination = $destinations->where('uuid', $request->destination_uuid)->first();
if (! $destination) {
return response()->json([
'message' => 'Validation failed.',
'errors' => [
'destination_uuid' => 'Provided destination_uuid does not belong to the specified server.',
],
], 422);
}
}
if (! isBase64Encoded($request->docker_compose_raw)) {
return response()->json([
'message' => 'Validation failed.',

View file

@ -70,6 +70,9 @@ class Advanced extends Component
#[Validate(['string', 'nullable'])]
public ?string $customInternalName = null;
#[Validate(['string', 'nullable'])]
public ?string $customContainerNamePrefix = null;
#[Validate(['boolean'])]
public bool $isGzipEnabled = true;
@ -111,6 +114,7 @@ class Advanced extends Component
$this->application->settings->is_build_server_enabled = $this->isBuildServerEnabled;
$this->application->settings->is_consistent_container_name_enabled = $this->isConsistentContainerNameEnabled;
$this->application->settings->custom_internal_name = $this->customInternalName;
$this->application->settings->custom_container_name_prefix = $this->customContainerNamePrefix;
$this->application->settings->is_gzip_enabled = $this->isGzipEnabled;
$this->application->settings->is_stripprefix_enabled = $this->isStripprefixEnabled;
$this->application->settings->is_raw_compose_deployment_enabled = $this->isRawComposeDeploymentEnabled;
@ -139,6 +143,7 @@ class Advanced extends Component
$this->isBuildServerEnabled = $this->application->settings->is_build_server_enabled;
$this->isConsistentContainerNameEnabled = $this->application->settings->is_consistent_container_name_enabled;
$this->customInternalName = $this->application->settings->custom_internal_name;
$this->customContainerNamePrefix = $this->application->settings->custom_container_name_prefix;
$this->isRawComposeDeploymentEnabled = $this->application->settings->is_raw_compose_deployment_enabled;
$this->isConnectToDockerNetworkEnabled = $this->application->settings->connect_to_docker_network;
$this->disableBuildCache = $this->application->settings->disable_build_cache;
@ -231,12 +236,19 @@ class Advanced extends Component
return;
}
$this->customContainerNamePrefix = null;
$customInternalName = $this->customInternalName;
$server = $this->application->destination->server;
$allApplications = $server->applications();
$foundSameInternalName = $allApplications->filter(function ($application) {
return $application->id !== $this->application->id && $application->settings->custom_internal_name === $this->customInternalName;
if ($application->id === $this->application->id) {
return false;
}
return $application->settings->custom_internal_name === $this->customInternalName
|| $application->settings->custom_container_name_prefix === $this->customInternalName;
});
if ($foundSameInternalName->isNotEmpty()) {
$this->dispatch('error', 'This custom container name is already in use by another application on this server.');
@ -252,6 +264,50 @@ class Advanced extends Component
}
}
public function saveCustomNamePrefix()
{
try {
$this->authorize('update', $this->application);
if (str($this->customContainerNamePrefix)->isNotEmpty()) {
$this->customContainerNamePrefix = str($this->customContainerNamePrefix)->slug()->value();
} else {
$this->customContainerNamePrefix = null;
}
if (is_null($this->customContainerNamePrefix)) {
$this->syncData(true);
$this->dispatch('success', 'Custom container name prefix saved.');
return;
}
$this->customInternalName = null;
$server = $this->application->destination->server;
$allApplications = $server->applications();
$foundDuplicate = $allApplications->filter(function ($application) {
if ($application->id === $this->application->id) {
return false;
}
return $application->settings->custom_container_name_prefix === $this->customContainerNamePrefix
|| $application->settings->custom_internal_name === $this->customContainerNamePrefix;
});
if ($foundDuplicate->isNotEmpty()) {
$this->dispatch('error', 'This custom container name prefix is already in use by another application on this server.');
return;
}
$this->syncData(true);
$this->dispatch('success', 'Custom container name prefix saved.');
} catch (\Throwable $e) {
return handleError($e, $this);
}
}
public function render()
{
return view('livewire.project.application.advanced');

View file

@ -181,6 +181,7 @@ function generateApplicationContainerName(Application $application, $pull_reques
// TODO: refactor generateApplicationContainerName, we do not need $application and $pull_request_id
$consistent_container_name = $application->settings->is_consistent_container_name_enabled;
$custom_container_name_prefix = $application->settings->custom_container_name_prefix;
$now = now()->format('Hisu');
if ($pull_request_id !== 0 && $pull_request_id !== null) {
return $application->uuid.'-pr-'.$pull_request_id;
@ -189,7 +190,10 @@ function generateApplicationContainerName(Application $application, $pull_reques
return $application->uuid;
}
return $application->uuid.'-'.$now;
$prefix = $custom_container_name_prefix ?? $application->uuid;
return $prefix.'-'.$now;
}
}
function get_port_from_dockerfile($dockerfile): ?int

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_settings', function (Blueprint $table) {
$table->string('custom_container_name_prefix')->nullable();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('application_settings', function (Blueprint $table) {
$table->dropColumn('custom_container_name_prefix');
});
}
};

View file

@ -63,10 +63,20 @@
@if ($isConsistentContainerNameEnabled === false)
<form class="flex items-end gap-2 " wire:submit.prevent='saveCustomName'>
<x-forms.input
helper="You can add a custom name for your container.<br><br>The name will be converted to slug format when you save it. <span class='font-bold dark:text-warning'>You will lose the rolling update feature!</span>"
instantSave id="customInternalName" label="Custom Container Name" canGate="update"
:canResource="$application" />
<x-forms.button canGate="update" :canResource="$application" type="submit">Save</x-forms.button>
helper="You can add a custom name for your container.<br><br>The name will be converted to slug format when you save it. <span class='font-bold dark:text-warning'>You will lose the rolling update feature!</span><br><br><span class='dark:text-red-500 text-red-600'>Cannot be used together with Custom Container Name Prefix.</span>"
id="customInternalName" label="Custom Container Name" canGate="update"
:canResource="$application" :disabled="!empty($customContainerNamePrefix)" />
<x-forms.button canGate="update" :canResource="$application" type="submit"
:disabled="!empty($customContainerNamePrefix)">Save</x-forms.button>
</form>
<form class="flex items-end gap-2 " wire:submit.prevent='saveCustomNamePrefix'>
<x-forms.input
helper="Set a custom prefix for your container name. The final name will be: <span class='font-bold'>your-prefix-timestamp</span>.<br><br>The name will be converted to slug format when you save it.<br><br>This option <span class='font-bold dark:text-success'>keeps rolling updates working!</span><br><br><span class='dark:text-red-500 text-red-600'>Cannot be used together with Custom Container Name.</span>"
id="customContainerNamePrefix" label="Custom Container Name Prefix" canGate="update"
:canResource="$application" placeholder="e.g. my-nextjs-app"
:disabled="!empty($customInternalName)" />
<x-forms.button canGate="update" :canResource="$application" type="submit"
:disabled="!empty($customInternalName)">Save</x-forms.button>
</form>
@endif
@if ($application->build_pack === 'dockercompose')

View file

@ -527,6 +527,7 @@
@endif
</div>
<h3 class="pt-8">Labels</h3>
@if ($application->settings->is_container_label_readonly_enabled)
<x-forms.textarea readonly disabled label="Container Labels" rows="15" id="customLabels"
monacoEditorLanguage="ini" useMonacoEditor x-bind:disabled="!canUpdate"></x-forms.textarea>

View file

@ -7,7 +7,7 @@
services:
n8n:
image: n8nio/n8n:2.1.5
image: n8nio/n8n:2.10.2
environment:
- SERVICE_URL_N8N_5678
- N8N_EDITOR_BASE_URL=${SERVICE_URL_N8N}
@ -54,7 +54,7 @@ services:
retries: 10
n8n-worker:
image: n8nio/n8n:2.1.5
image: n8nio/n8n:2.10.2
command: worker
environment:
- GENERIC_TIMEZONE=${GENERIC_TIMEZONE:-UTC}
@ -122,7 +122,7 @@ services:
retries: 10
task-runners:
image: n8nio/runners:2.1.5
image: n8nio/runners:2.10.2
environment:
- N8N_RUNNERS_TASK_BROKER_URI=${N8N_RUNNERS_TASK_BROKER_URI:-http://n8n-worker:5679}
- N8N_RUNNERS_AUTH_TOKEN=$SERVICE_PASSWORD_N8N

View file

@ -7,7 +7,7 @@
services:
n8n:
image: n8nio/n8n:2.1.5
image: n8nio/n8n:2.10.2
environment:
- SERVICE_URL_N8N_5678
- N8N_EDITOR_BASE_URL=${SERVICE_URL_N8N}
@ -47,7 +47,7 @@ services:
retries: 10
task-runners:
image: n8nio/runners:2.1.5
image: n8nio/runners:2.10.2
environment:
- N8N_RUNNERS_TASK_BROKER_URI=${N8N_RUNNERS_TASK_BROKER_URI:-http://n8n:5679}
- N8N_RUNNERS_AUTH_TOKEN=$SERVICE_PASSWORD_N8N

View file

@ -7,7 +7,7 @@
services:
n8n:
image: n8nio/n8n:2.1.5
image: n8nio/n8n:2.10.2
environment:
- SERVICE_URL_N8N_5678
- N8N_EDITOR_BASE_URL=${SERVICE_URL_N8N}
@ -38,7 +38,7 @@ services:
retries: 10
task-runners:
image: n8nio/runners:2.1.5
image: n8nio/runners:2.10.2
environment:
- N8N_RUNNERS_TASK_BROKER_URI=${N8N_RUNNERS_TASK_BROKER_URI:-http://n8n:5679}
- N8N_RUNNERS_AUTH_TOKEN=${SERVICE_PASSWORD_N8N}