From 248eadfe49ae09f43d5baf58e036fc0bdc42e328 Mon Sep 17 00:00:00 2001 From: Roman Pominov Date: Wed, 12 Oct 2022 10:58:02 +0400 Subject: [PATCH] rename user-content -> build --- apps/designer/.env.example | 11 ++-- apps/designer/app/designer/designer.tsx | 22 +++---- apps/designer/app/entry.server.tsx | 4 +- apps/designer/app/env.server.ts | 3 +- apps/designer/app/routes/$.tsx | 19 +++--- .../app/routes/designer/$projectId.tsx | 4 +- ...nt-params.test.ts => build-params.test.ts} | 64 +++++++++---------- ...user-content-params.ts => build-params.ts} | 40 ++++++------ .../designer/app/shared/router-utils/index.ts | 2 +- apps/designer/env-check.js | 8 +-- 10 files changed, 86 insertions(+), 91 deletions(-) rename apps/designer/app/shared/router-utils/{user-content-params.test.ts => build-params.test.ts} (54%) rename apps/designer/app/shared/router-utils/{user-content-params.ts => build-params.ts} (59%) diff --git a/apps/designer/.env.example b/apps/designer/.env.example index 09d3c97c9c..73d828b969 100644 --- a/apps/designer/.env.example +++ b/apps/designer/.env.example @@ -27,11 +27,12 @@ PUBLISHER_ENDPOINT= PUBLISHER_HOST= PUBLISHER_TOKEN= -# Base URL for accessing user-generated content (canvas / preview / etc) +# Base origin for accessing user-generated build (canvas / preview / etc) # Should contatin protocol and host (e.g. https://example.com) # Will be detected automatically in development environments like localhost or preview deployment -USER_CONTENT_BASE_URL= +BUILD_ORIGIN= -# If set to `true`, user-generated content will be available -# only at {project.domain} subdomain of {USER_CONTENT_BASE_URL} -USER_CONTENT_REQUIRE_SUBDOMAIN= +# If set to `true`, user-generated build will be available +# only at {project.domain} subdomain of {BUILD_ORIGIN}. +# This is used in production deployment for security reasons. +BUILD_REQUIRE_SUBDOMAIN= diff --git a/apps/designer/app/designer/designer.tsx b/apps/designer/app/designer/designer.tsx index efc3c8ca10..e69252688c 100644 --- a/apps/designer/app/designer/designer.tsx +++ b/apps/designer/app/designer/designer.tsx @@ -260,7 +260,7 @@ export type DesignerProps = { project: Project; pages: Pages; pageId: string; - userContentBaseUrl: string; + buildOrigin: string; }; export const Designer = ({ @@ -268,7 +268,7 @@ export const Designer = ({ project, pages, pageId, - userContentBaseUrl, + buildOrigin, }: DesignerProps) => { useSubscribeSyncStatus(); useSubscribeRootInstance(); @@ -304,19 +304,19 @@ export const Designer = ({ return page; }, [pages, pageId]); - const userContentUrl = new URL(userContentBaseUrl); - userContentUrl.pathname = page.path; - if (env.USER_CONTENT_REQUIRE_SUBDOMAIN) { - userContentUrl.host = `${project.domain}.${userContentUrl.host}`; + const buildUrl = new URL(buildOrigin); + buildUrl.pathname = page.path; + if (env.BUILD_REQUIRE_SUBDOMAIN) { + buildUrl.host = `${project.domain}.${buildUrl.host}`; } else { - userContentUrl.searchParams.set("projectId", project.id); + buildUrl.searchParams.set("projectId", project.id); } - userContentUrl.searchParams.set("mode", "edit"); - const canvasUrl = userContentUrl.toString(); + buildUrl.searchParams.set("mode", "edit"); + const canvasUrl = buildUrl.toString(); - userContentUrl.searchParams.set("mode", "preview"); - const previewUrl = userContentUrl.toString(); + buildUrl.searchParams.set("mode", "preview"); + const previewUrl = buildUrl.toString(); return ( diff --git a/apps/designer/app/entry.server.tsx b/apps/designer/app/entry.server.tsx index 92568534ee..a92df81742 100644 --- a/apps/designer/app/entry.server.tsx +++ b/apps/designer/app/entry.server.tsx @@ -4,7 +4,7 @@ import { initSentry } from "./shared/sentry"; import { prisma } from "@webstudio-is/prisma-client"; import { handleRequest as handleRequestDesigner } from "./shared/remix"; import { handleRequest as handleRequestCanvas } from "@webstudio-is/react-sdk"; -import { getUserContentParams } from "./shared/router-utils"; +import { getBuildParams } from "./shared/router-utils"; initSentry({ integrations: [new Sentry.Integrations.Prisma({ client: prisma })], @@ -23,7 +23,7 @@ export default function handleRequest( (match) => match.route.id === "routes/$" || match.route.id === "routes/index" ) && - getUserContentParams(request) !== undefined + getBuildParams(request) !== undefined ) { handle = handleRequestCanvas; } diff --git a/apps/designer/app/env.server.ts b/apps/designer/app/env.server.ts index fe28bd11a8..06738d30d0 100644 --- a/apps/designer/app/env.server.ts +++ b/apps/designer/app/env.server.ts @@ -8,8 +8,7 @@ const env = { DESIGNER_HOST: process.env.DESIGNER_HOST, PUBLISHER_ENDPOINT: process.env.PUBLISHER_ENDPOINT || null, PUBLISHER_HOST: process.env.PUBLISHER_HOST || null, - USER_CONTENT_REQUIRE_SUBDOMAIN: - process.env.USER_CONTENT_REQUIRE_SUBDOMAIN === "true", + BUILD_REQUIRE_SUBDOMAIN: process.env.BUILD_REQUIRE_SUBDOMAIN === "true", } as const; export default env; diff --git a/apps/designer/app/routes/$.tsx b/apps/designer/app/routes/$.tsx index 646b801c9c..c72ca92da0 100644 --- a/apps/designer/app/routes/$.tsx +++ b/apps/designer/app/routes/$.tsx @@ -13,13 +13,10 @@ import env, { Env } from "~/env.server"; import { sentryException } from "~/shared/sentry"; import { Canvas } from "~/canvas"; import { ErrorMessage } from "~/shared/error"; -import { - type CanvasRouteMode, - getUserContentParams, -} from "~/shared/router-utils"; +import { type BuildMode, getBuildParams } from "~/shared/router-utils"; type Data = - | (CanvasData & { env: Env; mode: CanvasRouteMode }) + | (CanvasData & { env: Env; mode: BuildMode }) | { errors: string; env: Env }; export const meta: MetaFunction = ({ data }: { data: Data }) => { @@ -34,18 +31,18 @@ export const loader: LoaderFunction = async ({ request, }): Promise => { try { - const userContentParams = getUserContentParams(request); + const buildParams = getBuildParams(request); - if (userContentParams === undefined) { + if (buildParams === undefined) { return redirect(config.dashboardPath); } - const { mode, pathname } = userContentParams; + const { mode, pathname } = buildParams; const project = - "projectId" in userContentParams - ? await db.project.loadById(userContentParams.projectId) - : await db.project.loadByDomain(userContentParams.projectDomain); + "projectId" in buildParams + ? await db.project.loadById(buildParams.projectId) + : await db.project.loadByDomain(buildParams.projectDomain); if (project === null) { throw json("Project not found", { status: 404 }); diff --git a/apps/designer/app/routes/designer/$projectId.tsx b/apps/designer/app/routes/designer/$projectId.tsx index eb9b7371f3..bbd7f8415b 100644 --- a/apps/designer/app/routes/designer/$projectId.tsx +++ b/apps/designer/app/routes/designer/$projectId.tsx @@ -6,7 +6,7 @@ import config from "~/config"; import { ErrorMessage } from "~/shared/error"; import { action, useAction } from "./_assets"; import { sentryException } from "~/shared/sentry"; -import { getUserContentBaseUrl } from "~/shared/router-utils"; +import { getBuildOrigin } from "~/shared/router-utils"; export { action, links }; @@ -35,7 +35,7 @@ export const loader: LoaderFunction = async ({ project, pages: devBuild.pages, pageId: pageIdParam || devBuild.pages.homePage.id, - userContentBaseUrl: getUserContentBaseUrl(request), + buildOrigin: getBuildOrigin(request), }; } catch (error) { sentryException({ error }); diff --git a/apps/designer/app/shared/router-utils/user-content-params.test.ts b/apps/designer/app/shared/router-utils/build-params.test.ts similarity index 54% rename from apps/designer/app/shared/router-utils/user-content-params.test.ts rename to apps/designer/app/shared/router-utils/build-params.test.ts index 17df7c0dac..5e0a7b93a9 100644 --- a/apps/designer/app/shared/router-utils/user-content-params.test.ts +++ b/apps/designer/app/shared/router-utils/build-params.test.ts @@ -1,7 +1,4 @@ -import { - getUserContentParams, - getUserContentBaseUrl, -} from "./user-content-params"; +import { getBuildParams, getBuildOrigin } from "./build-params"; const makeRequest = (url: string, headers: { [key: string]: string } = {}) => ({ url, @@ -21,8 +18,8 @@ const makeRequest = (url: string, headers: { [key: string]: string } = {}) => ({ const originalEnv = { ...process.env }; afterEach(() => { for (const name of [ - "USER_CONTENT_BASE_URL", - "USER_CONTENT_REQUIRE_SUBDOMAIN", + "BUILD_ORIGIN", + "BUILD_REQUIRE_SUBDOMAIN", "VERCEL_ENV", "VERCEL_URL", "NODE_ENV", @@ -37,31 +34,31 @@ afterEach(() => { } }); -describe("getUserContentBaseUrl", () => { +describe("getBuildOrigin", () => { test("Normal operation", () => { - process.env.USER_CONTENT_BASE_URL = "https://foo.com"; - expect(getUserContentBaseUrl(makeRequest("https://bar.com"))).toBe( + process.env.BUILD_ORIGIN = "https://foo.com"; + expect(getBuildOrigin(makeRequest("https://bar.com"))).toBe( "https://foo.com" ); }); test("Local development", () => { process.env.NODE_ENV = "development"; - expect(getUserContentBaseUrl(makeRequest("http://localhost"))).toBe( + expect(getBuildOrigin(makeRequest("http://localhost"))).toBe( "http://localhost" ); - expect(getUserContentBaseUrl(makeRequest("http://localhost:1234"))).toBe( + expect(getBuildOrigin(makeRequest("http://localhost:1234"))).toBe( + "http://localhost:1234" + ); + expect(getBuildOrigin(makeRequest("http://foo.localhost:1234"))).toBe( "http://localhost:1234" ); - expect( - getUserContentBaseUrl(makeRequest("http://foo.localhost:1234")) - ).toBe("http://localhost:1234"); }); test("Vercel preview", () => { process.env.VERCEL_ENV = "preview"; process.env.VERCEL_URL = "bar.foo.com"; - expect(getUserContentBaseUrl(makeRequest("https://baz.com"))).toBe( + expect(getBuildOrigin(makeRequest("https://baz.com"))).toBe( "https://bar.foo.com" ); }); @@ -69,60 +66,59 @@ describe("getUserContentBaseUrl", () => { describe("getCanvasRequestParams", () => { beforeEach(() => { - process.env.USER_CONTENT_BASE_URL = "https://foo.com"; + process.env.BUILD_ORIGIN = "https://foo.com"; }); test("detects project domain", () => { const request = makeRequest("https://bar.foo.com"); expect( - (getUserContentParams(request) as { projectDomain: string }).projectDomain + (getBuildParams(request) as { projectDomain: string }).projectDomain ).toBe("bar"); }); test("doesn't detect project domain if base domain is not a user content domain", () => { const request = makeRequest("https://bar.baz.com"); - expect(getUserContentParams(request)).toBeUndefined(); + expect(getBuildParams(request)).toBeUndefined(); }); test("detects project id", () => { const request = makeRequest("https://foo.com?projectId=123"); - expect( - (getUserContentParams(request) as { projectId: string }).projectId - ).toBe("123"); + expect((getBuildParams(request) as { projectId: string }).projectId).toBe( + "123" + ); }); test("doesn't detect project id if domain is not a user content domain", () => { const request = makeRequest("https://baz.com?projectId=123"); - expect(getUserContentParams(request)).toBeUndefined(); + expect(getBuildParams(request)).toBeUndefined(); }); - test("doesn't detect project id if USER_CONTENT_REQUIRE_SUBDOMAIN is true", () => { - process.env.USER_CONTENT_REQUIRE_SUBDOMAIN = "true"; + test("doesn't detect project id if BUILD_REQUIRE_SUBDOMAIN is true", () => { + process.env.BUILD_REQUIRE_SUBDOMAIN = "true"; const request = makeRequest("https://foo.com?projectId=123"); - expect(getUserContentParams(request)).toBeUndefined(); + expect(getBuildParams(request)).toBeUndefined(); }); test("detects path", () => { + expect(getBuildParams(makeRequest("https://bar.foo.com"))?.pathname).toBe( + "/" + ); expect( - getUserContentParams(makeRequest("https://bar.foo.com"))?.pathname - ).toBe("/"); - expect( - getUserContentParams(makeRequest("https://bar.foo.com/abc"))?.pathname + getBuildParams(makeRequest("https://bar.foo.com/abc"))?.pathname ).toBe("/abc"); expect( - getUserContentParams(makeRequest("https://bar.foo.com/abc/123"))?.pathname + getBuildParams(makeRequest("https://bar.foo.com/abc/123"))?.pathname ).toBe("/abc/123"); }); test("detects mode", () => { expect( - getUserContentParams(makeRequest("https://bar.foo.com?mode=edit"))?.mode + getBuildParams(makeRequest("https://bar.foo.com?mode=edit"))?.mode ).toBe("edit"); expect( - getUserContentParams(makeRequest("https://bar.foo.com?mode=preview")) - ?.mode + getBuildParams(makeRequest("https://bar.foo.com?mode=preview"))?.mode ).toBe("preview"); - expect(getUserContentParams(makeRequest("https://bar.foo.com"))?.mode).toBe( + expect(getBuildParams(makeRequest("https://bar.foo.com"))?.mode).toBe( "published" ); }); diff --git a/apps/designer/app/shared/router-utils/user-content-params.ts b/apps/designer/app/shared/router-utils/build-params.ts similarity index 59% rename from apps/designer/app/shared/router-utils/user-content-params.ts rename to apps/designer/app/shared/router-utils/build-params.ts index 36b5f0c31a..be9c6636f7 100644 --- a/apps/designer/app/shared/router-utils/user-content-params.ts +++ b/apps/designer/app/shared/router-utils/build-params.ts @@ -1,6 +1,6 @@ -export type CanvasRouteMode = "edit" | "preview" | "published"; -const modes = ["edit", "preview", "published"] as CanvasRouteMode[]; -const getMode = (url: URL): CanvasRouteMode => { +export type BuildMode = "edit" | "preview" | "published"; +const modes = ["edit", "preview", "published"] as BuildMode[]; +const getMode = (url: URL): BuildMode => { const modeParam = url.searchParams.get("mode"); const mode = modeParam === null ? "published" : modes.find((mode) => mode === modeParam); @@ -19,16 +19,19 @@ type MinimalRequest = { const getRequestHost = (request: MinimalRequest): string => request.headers.get("x-forwarded-host") || request.headers.get("host") || ""; -export const getUserContentBaseUrl = (request: MinimalRequest): string => { - const { USER_CONTENT_BASE_URL } = process.env; - if (USER_CONTENT_BASE_URL !== undefined && USER_CONTENT_BASE_URL !== "") { - return USER_CONTENT_BASE_URL; +export const getBuildOrigin = ( + request: MinimalRequest, + env = process.env +): string => { + const { BUILD_ORIGIN } = env; + if (BUILD_ORIGIN !== undefined && BUILD_ORIGIN !== "") { + return BUILD_ORIGIN; } // Local development special case const host = getRequestHost(request); if ( - process.env.NODE_ENV === "development" && + env.NODE_ENV === "development" && /^(.*\.)?localhost(:\d+)?$/.test(host) ) { return `http://${host.split(".").pop()}`; @@ -36,38 +39,37 @@ export const getUserContentBaseUrl = (request: MinimalRequest): string => { // Vercel preview special case if ( - (process.env.VERCEL_ENV === "preview" || - process.env.VERCEL_ENV === "development") && - typeof process.env.VERCEL_URL === "string" + (env.VERCEL_ENV === "preview" || env.VERCEL_ENV === "development") && + typeof env.VERCEL_URL === "string" ) { - return `https://${process.env.VERCEL_URL}`; + return `https://${env.VERCEL_URL}`; } throw new Error("Could not determine user content host"); }; -export const getUserContentParams = ( +export const getBuildParams = ( request: MinimalRequest ): | { projectId: string; - mode: CanvasRouteMode; + mode: BuildMode; pathname: string; } | { projectDomain: string; - mode: CanvasRouteMode; + mode: BuildMode; pathname: string; } | undefined => { const url = new URL(request.url); const requestHost = getRequestHost(request); - const userContentHost = new URL(getUserContentBaseUrl(request)).host; + const buildHost = new URL(getBuildOrigin(request)).host; - if (process.env.USER_CONTENT_REQUIRE_SUBDOMAIN !== "true") { + if (process.env.BUILD_REQUIRE_SUBDOMAIN !== "true") { const projectId = url.searchParams.get("projectId"); - if (projectId !== null && userContentHost === requestHost) { + if (projectId !== null && buildHost === requestHost) { return { projectId, mode: getMode(url), @@ -78,7 +80,7 @@ export const getUserContentParams = ( const [projectDomain, ...rest] = requestHost.split("."); const baseHost = rest.join("."); - if (baseHost === userContentHost) { + if (baseHost === buildHost) { return { projectDomain, mode: getMode(url), diff --git a/apps/designer/app/shared/router-utils/index.ts b/apps/designer/app/shared/router-utils/index.ts index 457ffb59c0..9eb5302fd6 100644 --- a/apps/designer/app/shared/router-utils/index.ts +++ b/apps/designer/app/shared/router-utils/index.ts @@ -1 +1 @@ -export * from "./user-content-params"; +export * from "./build-params"; diff --git a/apps/designer/env-check.js b/apps/designer/env-check.js index d1af7b43f6..aa5821bfef 100644 --- a/apps/designer/env-check.js +++ b/apps/designer/env-check.js @@ -40,14 +40,14 @@ if (process.env.DEPLOYMENT_ENVIRONMENT === "production") { "👉 In production DESIGNER_HOST is required for website functionality. Please set it to your production URL of the designer." ); } - if (!process.env.USER_CONTENT_BASE_URL) { + if (!process.env.BUILD_ORIGIN) { errors.push( - "👉 In production USER_CONTENT_BASE_URL is required for security reasons." + "👉 In production BUILD_ORIGIN is required for security reasons." ); } - if (process.env.USER_CONTENT_REQUIRE_SUBDOMAIN !== "true") { + if (process.env.BUILD_REQUIRE_SUBDOMAIN !== "true") { errors.push( - "👉 In production USER_CONTENT_REQUIRE_SUBDOMAIN must be set to 'true' for security reasons." + "👉 In production BUILD_REQUIRE_SUBDOMAIN must be set to 'true' for security reasons." ); } }