mirror of
https://github.com/webstudio-is/webstudio.git
synced 2026-11-03 10:37:48 +09:00
- Changed safe mode detection from .has() to strict === 'true' check - Made safemode a required boolean parameter in builderPath/builderUrl - Added 'Open in safe mode' option to dashboard project menu - Added safe mode indicator button with shield icon in topbar - Safe mode now prevents all script execution in HTML embeds - Added isSafeMode to React SDK context - Added test coverage for safe mode in HTML embed component ## Description 1. What is this PR about (link the issue and add a short description) ## Steps for reproduction 1. click button 2. expect xyz ## Code Review - [ ] hi @kof, I need you to do - conceptual review (architecture, feature-correctness) - detailed review (read every line) - test it on preview ## Before requesting a review - [ ] made a self-review - [ ] added inline comments where things may be not obvious (the "why", not "what") ## Before merging - [ ] tested locally and on preview environment (preview dev login: 0000) - [ ] updated [test cases](https://github.com/webstudio-is/webstudio/blob/main/apps/builder/docs/test-cases.md) document - [ ] added tests - [ ] if any new env variables are added, added them to `.env` file
181 lines
4.3 KiB
TypeScript
181 lines
4.3 KiB
TypeScript
import type { AUTH_PROVIDERS } from "~/shared/session";
|
|
import { publicStaticEnv } from "~/env/env.static";
|
|
import { getAuthorizationServerOrigin } from "./origins";
|
|
import type { BuilderMode } from "../nano-states/misc";
|
|
|
|
const searchParams = (params: Record<string, string | undefined | null>) => {
|
|
const searchParams = new URLSearchParams();
|
|
for (const [key, value] of Object.entries(params)) {
|
|
if (typeof value === "string") {
|
|
searchParams.set(key, value);
|
|
}
|
|
}
|
|
const asString = searchParams.toString();
|
|
return asString === "" ? "" : `?${asString}`;
|
|
};
|
|
|
|
export const builderPath = ({
|
|
pageId,
|
|
authToken,
|
|
pageHash,
|
|
mode,
|
|
safemode = false,
|
|
}: {
|
|
pageId?: string;
|
|
authToken?: string;
|
|
pageHash?: string;
|
|
mode?: "preview" | "content";
|
|
safemode?: boolean;
|
|
} = {}) => {
|
|
return `/${searchParams({
|
|
pageId,
|
|
authToken,
|
|
pageHash,
|
|
mode,
|
|
safemode: safemode ? "true" : undefined,
|
|
})}`;
|
|
};
|
|
|
|
export const builderUrl = (props: {
|
|
projectId: string;
|
|
pageId?: string;
|
|
origin: string;
|
|
authToken?: string;
|
|
mode?: BuilderMode;
|
|
safemode?: boolean;
|
|
}) => {
|
|
const authServerOrigin = getAuthorizationServerOrigin(props.origin);
|
|
|
|
const url = new URL(
|
|
builderPath({
|
|
pageId: props.pageId,
|
|
authToken: props.authToken,
|
|
safemode: props.safemode,
|
|
}),
|
|
authServerOrigin
|
|
);
|
|
|
|
const fragments = url.host.split(".");
|
|
if (fragments.length <= 3) {
|
|
fragments.splice(0, 0, "p-" + props.projectId);
|
|
} else {
|
|
// staging | development branches
|
|
fragments[0] = "p-" + props.projectId + "-dot-" + fragments[0];
|
|
}
|
|
|
|
url.host = fragments.join(".");
|
|
|
|
if (props.mode !== undefined) {
|
|
url.searchParams.set("mode", props.mode);
|
|
}
|
|
|
|
return url.href;
|
|
};
|
|
|
|
export const dashboardPath = (
|
|
view: "templates" | "search" | "projects" = "projects"
|
|
) => {
|
|
if (view === "projects") {
|
|
return `/dashboard`;
|
|
}
|
|
return `/dashboard/${view}`;
|
|
};
|
|
|
|
export const dashboardUrl = (props: { origin: string }) => {
|
|
const authServerOrigin = getAuthorizationServerOrigin(props.origin);
|
|
|
|
return `${authServerOrigin}/dashboard`;
|
|
};
|
|
|
|
export const cloneProjectUrl = (props: {
|
|
origin: string;
|
|
sourceAuthToken: string;
|
|
}) => {
|
|
const authServerOrigin = getAuthorizationServerOrigin(props.origin);
|
|
|
|
const searchParams = new URLSearchParams();
|
|
searchParams.set("projectToCloneAuthToken", props.sourceAuthToken);
|
|
|
|
return `${authServerOrigin}/dashboard?${searchParams.toString()}`;
|
|
};
|
|
|
|
export const loginPath = (params: {
|
|
error?: (typeof AUTH_PROVIDERS)[keyof typeof AUTH_PROVIDERS];
|
|
message?: string;
|
|
returnTo?: string;
|
|
}) => `/login${searchParams(params)}`;
|
|
|
|
export const logoutPath = () => "/logout";
|
|
export const restLogoutPath = () => "/dashboard-logout";
|
|
|
|
export const userPlanSubscriptionPath = (subscriptionId?: string) => {
|
|
const urlSearchParams = new URLSearchParams();
|
|
urlSearchParams.set("return_url", window.location.href);
|
|
if (subscriptionId) {
|
|
urlSearchParams.set("subscription", subscriptionId);
|
|
}
|
|
|
|
return `/n8n/billing_portal/sessions?${urlSearchParams.toString()}`;
|
|
};
|
|
|
|
export const authCallbackPath = ({
|
|
provider,
|
|
}: {
|
|
provider: "google" | "github";
|
|
}) => `/auth/${provider}/callback`;
|
|
|
|
export const authPath = ({
|
|
provider,
|
|
}: {
|
|
provider: "google" | "github" | "dev";
|
|
}) => `/auth/${provider}`;
|
|
|
|
export const restAssetsPath = () => {
|
|
return `/rest/assets`;
|
|
};
|
|
|
|
export const restAssetsUploadPath = ({
|
|
name,
|
|
width,
|
|
height,
|
|
}: {
|
|
name: string;
|
|
width?: number | undefined;
|
|
height?: number | undefined;
|
|
}) => {
|
|
const urlSearchParams = new URLSearchParams();
|
|
if (width !== undefined) {
|
|
urlSearchParams.set("width", String(width));
|
|
}
|
|
if (height !== undefined) {
|
|
urlSearchParams.set("height", String(height));
|
|
}
|
|
|
|
if (urlSearchParams.size > 0) {
|
|
return `/rest/assets/${name}?${urlSearchParams.toString()}`;
|
|
}
|
|
|
|
return `/rest/assets/${name}`;
|
|
};
|
|
|
|
export const restPatchPath = () => {
|
|
const urlSearchParams = new URLSearchParams();
|
|
|
|
urlSearchParams.set("client-version", publicStaticEnv.VERSION);
|
|
|
|
const urlSearchParamsString = urlSearchParams.toString();
|
|
|
|
return `/rest/patch${
|
|
urlSearchParamsString ? `?${urlSearchParamsString}` : ""
|
|
}`;
|
|
};
|
|
|
|
export const getCanvasUrl = () => {
|
|
return `/canvas`;
|
|
};
|
|
|
|
export const restResourcesLoader = () => `/rest/resources-loader`;
|
|
|
|
export const marketplacePath = (method: string) =>
|
|
`/builder/marketplace/${method}`;
|