mirror of
https://github.com/Infisical/infisical.git
synced 2026-01-09 15:38:03 -05:00
Merge pull request #5037 from Infisical/feature/add-support-for-cdn-subdomain
feature(cdn): add optional CDN URL configuration for static assets in Vite
This commit is contained in:
@@ -229,6 +229,7 @@ const envSchema = z
|
||||
CAPTCHA_SECRET: zpStr(z.string().optional()),
|
||||
CAPTCHA_SITE_KEY: zpStr(z.string().optional()),
|
||||
INTERCOM_ID: zpStr(z.string().optional()),
|
||||
CDN_HOST: zpStr(z.string().optional()),
|
||||
|
||||
// TELEMETRY
|
||||
OTEL_TELEMETRY_COLLECTION_ENABLED: zodStrBool.default("false"),
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
|
||||
import staticServe from "@fastify/static";
|
||||
import RE2 from "re2";
|
||||
|
||||
import { getConfig, IS_PACKAGED } from "@app/lib/config/env";
|
||||
|
||||
@@ -15,6 +17,9 @@ export const registerServeUI = async (
|
||||
dir: string;
|
||||
}
|
||||
) => {
|
||||
const appCfg = getConfig();
|
||||
const cdnHost = appCfg.CDN_HOST || "";
|
||||
|
||||
// use this only for frontend runtime static non-sensitive configuration in standalone mode
|
||||
// that app needs before loading like posthog dsn key
|
||||
// for most of the other usecase use server config
|
||||
@@ -25,15 +30,26 @@ export const registerServeUI = async (
|
||||
hide: true
|
||||
},
|
||||
handler: (_req, res) => {
|
||||
const appCfg = getConfig();
|
||||
void res.type("application/javascript");
|
||||
const config = {
|
||||
CAPTCHA_SITE_KEY: appCfg.CAPTCHA_SITE_KEY,
|
||||
POSTHOG_API_KEY: appCfg.POSTHOG_PROJECT_API_KEY,
|
||||
INTERCOM_ID: appCfg.INTERCOM_ID,
|
||||
TELEMETRY_CAPTURING_ENABLED: appCfg.TELEMETRY_ENABLED
|
||||
TELEMETRY_CAPTURING_ENABLED: appCfg.TELEMETRY_ENABLED,
|
||||
CDN_HOST: cdnHost
|
||||
};
|
||||
const js = `window.__INFISICAL_RUNTIME_ENV__ = Object.freeze(${JSON.stringify(config)});`;
|
||||
// Define window.__toCdnUrl for Vite's experimental.renderBuiltUrl runtime support
|
||||
// This function is called by dynamically imported chunks to resolve CDN URLs
|
||||
const js = `
|
||||
window.__INFISICAL_RUNTIME_ENV__ = Object.freeze(${JSON.stringify(config)});
|
||||
window.__toCdnUrl = function(filename) {
|
||||
var cdnHost = window.__INFISICAL_RUNTIME_ENV__.CDN_HOST || "";
|
||||
if (cdnHost && filename.startsWith("assets/")) {
|
||||
return cdnHost + "/" + filename;
|
||||
}
|
||||
return "/" + filename;
|
||||
};
|
||||
`.trim();
|
||||
return res.send(js);
|
||||
}
|
||||
});
|
||||
@@ -41,6 +57,21 @@ export const registerServeUI = async (
|
||||
if (standaloneMode) {
|
||||
const frontendName = IS_PACKAGED ? "frontend" : "frontend-build";
|
||||
const frontendPath = path.join(dir, frontendName);
|
||||
|
||||
const indexHtmlPath = path.join(frontendPath, "index.html");
|
||||
let indexHtml = fs.readFileSync(indexHtmlPath, "utf-8");
|
||||
|
||||
if (cdnHost) {
|
||||
// Replace relative asset paths with CDN URLs in script and link tags
|
||||
indexHtml = indexHtml
|
||||
.replace(/src="\/assets\//g, `src="${cdnHost}/assets/`)
|
||||
.replace(/href="\/assets\//g, `href="${cdnHost}/assets/`);
|
||||
|
||||
indexHtml = indexHtml.replace(new RE2(`(__INFISICAL_CDN_HOST__)`, "g"), cdnHost);
|
||||
} else {
|
||||
indexHtml = indexHtml.replace(new RE2(`(__INFISICAL_CDN_HOST__)`, "g"), "");
|
||||
}
|
||||
|
||||
await server.register(staticServe, {
|
||||
root: frontendPath,
|
||||
wildcard: false,
|
||||
@@ -60,12 +91,12 @@ export const registerServeUI = async (
|
||||
return;
|
||||
}
|
||||
|
||||
return reply.sendFile("index.html", {
|
||||
immutable: false,
|
||||
maxAge: 0,
|
||||
lastModified: false,
|
||||
etag: false
|
||||
});
|
||||
return reply
|
||||
.type("text/html")
|
||||
.header("Cache-Control", "no-cache, no-store, must-revalidate")
|
||||
.header("Pragma", "no-cache")
|
||||
.header("Expires", "0")
|
||||
.send(indexHtml);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -8,15 +8,15 @@
|
||||
http-equiv="Content-Security-Policy"
|
||||
content="
|
||||
default-src 'self';
|
||||
connect-src 'self' https://d1zwf0dwl0k2ky.cloudfront.net https://*.posthog.com http://127.0.0.1:* https://cdn.jsdelivr.net/npm/@lottiefiles/dotlottie-web@0.38.2/dist/dotlottie-player.wasm;
|
||||
script-src 'self' https://d1zwf0dwl0k2ky.cloudfront.net https://*.posthog.com https://js.stripe.com https://api.stripe.com https://widget.intercom.io https://js.intercomcdn.com https://hcaptcha.com https://*.hcaptcha.com 'unsafe-inline' 'unsafe-eval' https://cdn.jsdelivr.net/npm/@lottiefiles/dotlottie-web@0.38.2/dist/dotlottie-player.wasm;
|
||||
style-src 'self' https://d1zwf0dwl0k2ky.cloudfront.net 'unsafe-inline' https://hcaptcha.com https://*.hcaptcha.com;
|
||||
connect-src 'self' __INFISICAL_CDN_HOST__ https://*.posthog.com http://127.0.0.1:* https://cdn.jsdelivr.net/npm/@lottiefiles/dotlottie-web@0.38.2/dist/dotlottie-player.wasm;
|
||||
script-src 'self' __INFISICAL_CDN_HOST__ https://*.posthog.com https://js.stripe.com https://api.stripe.com https://widget.intercom.io https://js.intercomcdn.com https://hcaptcha.com https://*.hcaptcha.com 'unsafe-inline' 'unsafe-eval' https://cdn.jsdelivr.net/npm/@lottiefiles/dotlottie-web@0.38.2/dist/dotlottie-player.wasm;
|
||||
style-src 'self' __INFISICAL_CDN_HOST__ 'unsafe-inline' https://hcaptcha.com https://*.hcaptcha.com;
|
||||
child-src https://api.stripe.com;
|
||||
frame-src https://js.stripe.com/ https://api.stripe.com https://www.youtube.com/ https://hcaptcha.com https://*.hcaptcha.com;
|
||||
connect-src 'self' https://d1zwf0dwl0k2ky.cloudfront.net wss://nexus-websocket-a.intercom.io https://api-iam.intercom.io https://api.heroku.com/ https://id.heroku.com/oauth/authorize https://id.heroku.com/oauth/token https://checkout.stripe.com https://app.posthog.com https://api.stripe.com https://api.pwnedpasswords.com http://127.0.0.1:* https://hcaptcha.com https://*.hcaptcha.com;
|
||||
img-src 'self' https://d1zwf0dwl0k2ky.cloudfront.net https://static.intercomassets.com https://js.intercomcdn.com https://downloads.intercomcdn.com https://*.stripe.com https://i.ytimg.com/ data:;
|
||||
media-src https://d1zwf0dwl0k2ky.cloudfront.net https://js.intercomcdn.com;
|
||||
font-src 'self' https://d1zwf0dwl0k2ky.cloudfront.net https://fonts.intercomcdn.com/ https://fonts.gstatic.com;
|
||||
connect-src 'self' __INFISICAL_CDN_HOST__ wss://nexus-websocket-a.intercom.io https://api-iam.intercom.io https://api.heroku.com/ https://id.heroku.com/oauth/authorize https://id.heroku.com/oauth/token https://checkout.stripe.com https://app.posthog.com https://api.stripe.com https://api.pwnedpasswords.com http://127.0.0.1:* https://hcaptcha.com https://*.hcaptcha.com;
|
||||
img-src 'self' https://static.intercomassets.com https://js.intercomcdn.com https://downloads.intercomcdn.com https://*.stripe.com https://i.ytimg.com/ data:;
|
||||
media-src __INFISICAL_CDN_HOST__ https://js.intercomcdn.com;
|
||||
font-src 'self' __INFISICAL_CDN_HOST__ https://fonts.intercomcdn.com/ https://fonts.gstatic.com;
|
||||
"
|
||||
/>
|
||||
<title>Infisical</title>
|
||||
|
||||
@@ -52,6 +52,15 @@ export default defineConfig(({ mode }) => {
|
||||
}
|
||||
}
|
||||
},
|
||||
experimental: {
|
||||
renderBuiltUrl(filename, { hostType }) {
|
||||
if (hostType === "js") {
|
||||
return { runtime: `window.__toCdnUrl(${JSON.stringify(filename)})` };
|
||||
}
|
||||
|
||||
return { relative: true };
|
||||
}
|
||||
},
|
||||
plugins: [
|
||||
tsconfigPaths(),
|
||||
nodePolyfills({
|
||||
|
||||
Reference in New Issue
Block a user