diff --git a/Dockerfile b/Dockerfile
index 2730dccb9..a605e47e1 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -24,12 +24,14 @@ ARG PUBLIC_APPWRITE_ENDPOINT
ARG PUBLIC_GROWTH_ENDPOINT
ARG PUBLIC_STRIPE_KEY
ARG SENTRY_AUTH_TOKEN
+ARG PUBLIC_PROJECT_PROFILE
ENV PUBLIC_APPWRITE_ENDPOINT=$PUBLIC_APPWRITE_ENDPOINT
ENV PUBLIC_GROWTH_ENDPOINT=$PUBLIC_GROWTH_ENDPOINT
ENV PUBLIC_CONSOLE_MODE=$PUBLIC_CONSOLE_MODE
ENV PUBLIC_STRIPE_KEY=$PUBLIC_STRIPE_KEY
ENV SENTRY_AUTH_TOKEN=$SENTRY_AUTH_TOKEN
+ENV PUBLIC_PROJECT_PROFILE=$PUBLIC_PROJECT_PROFILE
ENV NODE_OPTIONS=--max_old_space_size=8192
RUN pnpm run build
@@ -38,5 +40,5 @@ FROM nginx:1.25-alpine
EXPOSE 80
-COPY docker/nginx.conf /etc/nginx/conf.d/default.conf
+COPY docker/nginx.$PUBLIC_PROJECT_PROFILE.conf /etc/nginx/conf.d/default.conf
COPY --from=build /app/build /usr/share/nginx/html/console
\ No newline at end of file
diff --git a/docker/nginx.conf b/docker/nginx.console.conf
similarity index 100%
rename from docker/nginx.conf
rename to docker/nginx.console.conf
diff --git a/docker/nginx.studio.conf b/docker/nginx.studio.conf
new file mode 100644
index 000000000..1110b3b06
--- /dev/null
+++ b/docker/nginx.studio.conf
@@ -0,0 +1,39 @@
+map $sent_http_content_type $expires {
+ # cache everything for 1 year
+ default 1y;
+ # html files shouldn't be cached for single-page applications
+ text/html off;
+}
+
+server {
+ listen 80;
+ server_name localhost;
+
+ # serve compressed file if filename.gz exists
+ gzip_static on;
+
+ location /studio {
+ root /usr/share/nginx/html;
+ index index.html index.htm;
+ try_files $uri /studio/index.html;
+
+ # Add cache headers
+ expires $expires;
+ add_header Pragma public;
+ add_header Cache-Control "public";
+
+ # Deny IE browsers from going into quirks mode
+ add_header X-UA-Compatible "IE=Edge";
+ # X-Frame-Options is to prevent from clickJacking attack
+ add_header X-Frame-Options SAMEORIGIN;
+ # This header enables the Cross-site scripting (XSS) filter
+ add_header X-XSS-Protection "1; mode=block;";
+ # disable content-type sniffing on some browsers.
+ add_header X-Content-Type-Options nosniff;
+ }
+
+ location / {
+ absolute_redirect off;
+ return 301 /studio;
+ }
+}
\ No newline at end of file
diff --git a/package.json b/package.json
index 918029efb..cb3b5b87a 100644
--- a/package.json
+++ b/package.json
@@ -33,6 +33,7 @@
"cron-parser": "^4.9.0",
"dayjs": "^1.11.13",
"deep-equal": "^2.2.3",
+ "dotenv": "^16.4.7",
"echarts": "^5.6.0",
"envfile": "^7.1.0",
"ignore": "^6.0.2",
@@ -47,6 +48,7 @@
},
"devDependencies": {
"@eslint/compat": "^1.2.7",
+ "@eslint/js": "^9.23.0",
"@melt-ui/pp": "^0.3.2",
"@melt-ui/svelte": "^0.86.5",
"@playwright/test": "^1.51.1",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index aec88b29b..f0f349ed7 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -47,6 +47,9 @@ importers:
deep-equal:
specifier: ^2.2.3
version: 2.2.3
+ dotenv:
+ specifier: ^16.4.7
+ version: 16.4.7
echarts:
specifier: ^5.6.0
version: 5.6.0
@@ -84,6 +87,9 @@ importers:
'@eslint/compat':
specifier: ^1.2.7
version: 1.2.7(eslint@9.23.0)
+ '@eslint/js':
+ specifier: ^9.23.0
+ version: 9.23.0
'@melt-ui/pp':
specifier: ^0.3.2
version: 0.3.2(@melt-ui/svelte@0.86.5(svelte@5.25.3))(svelte@5.25.3)
diff --git a/src/app.html b/src/app.html
index 18a92b1fa..675d85d59 100644
--- a/src/app.html
+++ b/src/app.html
@@ -5,9 +5,9 @@
-
-
-
+
+
+
%sveltekit.head%
diff --git a/src/hooks.server.ts b/src/hooks.server.ts
index e54c183db..8c17e3e98 100644
--- a/src/hooks.server.ts
+++ b/src/hooks.server.ts
@@ -2,6 +2,7 @@ import { sequence } from '@sveltejs/kit/hooks';
import { handleErrorWithSentry, sentryHandle } from '@sentry/sveltekit';
import * as Sentry from '@sentry/sveltekit';
import { isCloud, isProd } from '$lib/system';
+import type { Handle } from '@sveltejs/kit';
Sentry.init({
enabled: isCloud && isProd,
@@ -9,6 +10,26 @@ Sentry.init({
tracesSampleRate: 1.0
});
-export const handle = sequence(sentryHandle());
+const projectProfile = process.env.PUBLIC_PROJECT_PROFILE || 'console';
+
+const dynamicHtmlTransform: Handle = async ({ event, resolve }) => {
+ let response = await resolve(event);
+
+ if (response.headers.get('content-type')?.includes('text/html')) {
+ let html = await response.text();
+
+ // Replace '/console/' dynamically with the project profile
+ html = html.replace(/\/{project_profile}\//g, `/${projectProfile}/`);
+
+ response = new Response(html, {
+ status: response.status,
+ headers: response.headers
+ });
+ }
+
+ return response;
+};
+
+export const handle = sequence(sentryHandle(), dynamicHtmlTransform);
export const handleError = handleErrorWithSentry();
diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte
index 3a1dad550..1934a9047 100644
--- a/src/routes/+layout.svelte
+++ b/src/routes/+layout.svelte
@@ -149,14 +149,14 @@
{#each preloadFonts as font}
{/each}
-
+
{#if isCloud}
{#each preloadFontsCloud as font}
{/each}
-
+
{/if}
diff --git a/svelte.config.js b/svelte.config.js
index 2939a3e1b..0c6a935ef 100644
--- a/svelte.config.js
+++ b/svelte.config.js
@@ -1,6 +1,9 @@
import adapter from '@sveltejs/adapter-static';
import { sveltePreprocess } from 'svelte-preprocess';
import { preprocessMeltUI, sequence } from '@melt-ui/pp';
+import 'dotenv/config';
+
+const projectProfile = process.env.PUBLIC_PROJECT_PROFILE || 'console';
/** @type {import('@sveltejs/kit').Config} */
const config = {
@@ -26,7 +29,7 @@ const config = {
precompress: true
}),
paths: {
- base: '/console'
+ base: `/${projectProfile}`
}
},
vitePlugin: {
diff --git a/vite.config.ts b/vite.config.ts
index d0a9f9bf0..47086a375 100644
--- a/vite.config.ts
+++ b/vite.config.ts
@@ -12,7 +12,7 @@ export default defineConfig({
project: 'console'
}
}),
- sveltekit()
+ sveltekit(),
],
optimizeDeps: {
include: ['echarts', 'prismjs']