diff --git a/app/Jobs/ApplicationDeploymentJob.php b/app/Jobs/ApplicationDeploymentJob.php
index 92186953b..b72d2ade3 100644
--- a/app/Jobs/ApplicationDeploymentJob.php
+++ b/app/Jobs/ApplicationDeploymentJob.php
@@ -2027,7 +2027,11 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
if (str($this->application->custom_nginx_configuration)->isNotEmpty()) {
$nginx_config = base64_encode($this->application->custom_nginx_configuration);
} else {
- $nginx_config = base64_encode(defaultNginxConfiguration());
+ if ($this->application->settings->is_spa) {
+ $nginx_config = base64_encode(defaultNginxConfiguration('spa'));
+ } else {
+ $nginx_config = base64_encode(defaultNginxConfiguration());
+ }
}
} else {
if ($this->application->build_pack === 'nixpacks') {
@@ -2094,7 +2098,11 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
if (str($this->application->custom_nginx_configuration)->isNotEmpty()) {
$nginx_config = base64_encode($this->application->custom_nginx_configuration);
} else {
- $nginx_config = base64_encode(defaultNginxConfiguration());
+ if ($this->application->settings->is_spa) {
+ $nginx_config = base64_encode(defaultNginxConfiguration('spa'));
+ } else {
+ $nginx_config = base64_encode(defaultNginxConfiguration());
+ }
}
}
$build_command = "docker build {$this->addHosts} --network host -f {$this->workdir}/Dockerfile {$this->build_args} --progress plain -t {$this->production_image_name} {$this->workdir}";
diff --git a/app/Livewire/Project/Application/General.php b/app/Livewire/Project/Application/General.php
index 1d58ed33a..b85023a0c 100644
--- a/app/Livewire/Project/Application/General.php
+++ b/app/Livewire/Project/Application/General.php
@@ -86,6 +86,7 @@ class General extends Component
'application.post_deployment_command_container' => 'nullable',
'application.custom_nginx_configuration' => 'nullable',
'application.settings.is_static' => 'boolean|required',
+ 'application.settings.is_spa' => 'boolean|required',
'application.settings.is_build_server_enabled' => 'boolean|required',
'application.settings.is_container_label_escape_enabled' => 'boolean|required',
'application.settings.is_container_label_readonly_enabled' => 'boolean|required',
@@ -124,6 +125,7 @@ class General extends Component
'application.docker_compose_custom_build_command' => 'Docker compose custom build command',
'application.custom_nginx_configuration' => 'Custom Nginx configuration',
'application.settings.is_static' => 'Is static',
+ 'application.settings.is_spa' => 'Is SPA',
'application.settings.is_build_server_enabled' => 'Is build server enabled',
'application.settings.is_container_label_escape_enabled' => 'Is container label escape enabled',
'application.settings.is_container_label_readonly_enabled' => 'Is container label readonly',
@@ -171,6 +173,9 @@ class General extends Component
public function instantSave()
{
+ if ($this->application->settings->isDirty('is_spa')) {
+ $this->generateNginxConfiguration($this->application->settings->is_spa ? 'spa' : 'static');
+ }
$this->application->settings->save();
$this->dispatch('success', 'Settings saved.');
$this->application->refresh();
@@ -190,6 +195,7 @@ class General extends Component
if ($this->application->settings->is_container_label_readonly_enabled) {
$this->resetDefaultLabels(false);
}
+
}
public function loadComposeFile($isInit = false)
@@ -287,9 +293,9 @@ class General extends Component
}
}
- public function generateNginxConfiguration()
+ public function generateNginxConfiguration($type = 'static')
{
- $this->application->custom_nginx_configuration = defaultNginxConfiguration();
+ $this->application->custom_nginx_configuration = defaultNginxConfiguration($type);
$this->application->save();
$this->dispatch('success', 'Nginx configuration generated.');
}
diff --git a/bootstrap/helpers/shared.php b/bootstrap/helpers/shared.php
index a020e7558..12bfccfaf 100644
--- a/bootstrap/helpers/shared.php
+++ b/bootstrap/helpers/shared.php
@@ -4061,9 +4061,35 @@ function isEmailRateLimited(string $limiterKey, int $decaySeconds = 3600, ?calla
return $rateLimited;
}
-function defaultNginxConfiguration(): string
+function defaultNginxConfiguration(string $type = 'static'): string
{
- return 'server {
+ if ($type === 'spa') {
+ return <<<'NGINX'
+server {
+ location / {
+ root /usr/share/nginx/html;
+ index index.html;
+ try_files $uri $uri/ /index.html;
+ }
+
+ # Handle 404 errors
+ error_page 404 /404.html;
+ location = /404.html {
+ root /usr/share/nginx/html;
+ internal;
+ }
+
+ # Handle server errors (50x)
+ error_page 500 502 503 504 /50x.html;
+ location = /50x.html {
+ root /usr/share/nginx/html;
+ internal;
+ }
+}
+NGINX;
+ } else {
+ return <<<'NGINX'
+server {
location / {
root /usr/share/nginx/html;
index index.html index.htm;
@@ -4083,7 +4109,9 @@ function defaultNginxConfiguration(): string
root /usr/share/nginx/html;
internal;
}
-}';
+}
+NGINX;
+ }
}
function convertGitUrl(string $gitRepository, string $deploymentType, ?GithubApp $source = null): array
diff --git a/database/migrations/2025_03_31_124212_add_specific_spa_configuration.php b/database/migrations/2025_03_31_124212_add_specific_spa_configuration.php
new file mode 100644
index 000000000..1ec0d722b
--- /dev/null
+++ b/database/migrations/2025_03_31_124212_add_specific_spa_configuration.php
@@ -0,0 +1,28 @@
+boolean('is_spa')->default(false);
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ */
+ public function down(): void
+ {
+ Schema::table('application_settings', function (Blueprint $table) {
+ $table->dropColumn('is_spa');
+ });
+ }
+};
diff --git a/resources/views/livewire/project/application/general.blade.php b/resources/views/livewire/project/application/general.blade.php
index 1cc71d063..8c12d1d62 100644
--- a/resources/views/livewire/project/application/general.blade.php
+++ b/resources/views/livewire/project/application/general.blade.php
@@ -69,6 +69,17 @@