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']