mirror of
https://github.com/webstudio-is/webstudio.git
synced 2026-10-23 03:27:27 +09:00
refactor: hardcode asset and upload path in builder (#4579)
We hardcoded asset base url in image loader. Though locally different one is not resolved properly. So aligned local and deployed image paths with /cgi/asset/ everywhere. Images are now uploaded to public/cgi/asset locally.
This commit is contained in:
1 parent
4034b401c3
commit
302f5daa7f
11 files changed
+27
-92
No files matched your search
@@ -11,10 +11,6 @@ DEV_LOGIN=true
|
||||
# Restrictions
|
||||
MAX_ASSETS_PER_PROJECT=50
|
||||
|
||||
# relative to current working directory
|
||||
FILE_UPLOAD_PATH="public/s/uploads"
|
||||
ASSET_BASE_URL=/s/uploads/
|
||||
|
||||
POSTGREST_URL=http://localhost:3000
|
||||
# JWT token, decode using jwt.io. Encoded with PGRST_JWT_SECRET from docker-compose.yml
|
||||
POSTGREST_API_KEY="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImttZHBpeHpvcWlpcmJtcGRpcHB5Iiwicm9sZSI6ImFub24iLCJpYXQiOjE2NjYzMzY0MjgsImV4cCI6MTk4MTkxMjQyOH0.jjeYvTDrWP9pV7dfZr6fptualNQ3aR13kuPhvT25Sso"
|
||||
|
||||
@@ -8,4 +8,5 @@
|
||||
/api/index.js.map
|
||||
/api/_assets
|
||||
/public/s/uploads
|
||||
/public/cgi/asset
|
||||
|
||||
@@ -3,7 +3,6 @@ import { ErrorBoundary, type FallbackProps } from "react-error-boundary";
|
||||
import { useStore } from "@nanostores/react";
|
||||
import type { Instances } from "@webstudio-is/sdk";
|
||||
import {
|
||||
type Params,
|
||||
type Components,
|
||||
coreMetas,
|
||||
corePropsMetas,
|
||||
@@ -49,6 +48,7 @@ import {
|
||||
$isDesignMode,
|
||||
$isContentMode,
|
||||
subscribeModifierKeys,
|
||||
assetBaseUrl,
|
||||
} from "~/shared/nano-states";
|
||||
import { useDragAndDrop } from "./shared/use-drag-drop";
|
||||
import {
|
||||
@@ -64,7 +64,6 @@ import { useMount } from "~/shared/hook-utils/use-mount";
|
||||
import { subscribeInterceptedEvents } from "./interceptor";
|
||||
import { subscribeCommands } from "~/canvas/shared/commands";
|
||||
import { updateCollaborativeInstanceRect } from "./collaborative-instance";
|
||||
import { $params } from "./stores";
|
||||
import { initCanvasApi } from "~/shared/canvas-api";
|
||||
import { subscribeFontLoadingDone } from "./shared/font-weight-support";
|
||||
import { useDebounceEffect } from "~/shared/hook-utils/use-debounce-effect";
|
||||
@@ -95,11 +94,7 @@ const FallbackComponent = ({ error, resetErrorBoundary }: FallbackProps) => {
|
||||
);
|
||||
};
|
||||
|
||||
const useElementsTree = (
|
||||
components: Components,
|
||||
instances: Instances,
|
||||
params: Params
|
||||
) => {
|
||||
const useElementsTree = (components: Components, instances: Instances) => {
|
||||
const page = useStore($selectedPage);
|
||||
const isPreviewMode = useStore($isPreviewMode);
|
||||
const rootInstanceId = page?.rootInstanceId ?? "";
|
||||
@@ -119,7 +114,7 @@ const useElementsTree = (
|
||||
<ReactSdkContext.Provider
|
||||
value={{
|
||||
renderer: isPreviewMode ? "preview" : "canvas",
|
||||
assetBaseUrl: params.assetBaseUrl,
|
||||
assetBaseUrl,
|
||||
imageLoader: wsImageLoader,
|
||||
resources: {},
|
||||
}}
|
||||
@@ -135,7 +130,7 @@ const useElementsTree = (
|
||||
})}
|
||||
</ReactSdkContext.Provider>
|
||||
);
|
||||
}, [params, instances, rootInstanceId, components, isPreviewMode]);
|
||||
}, [instances, rootInstanceId, components, isPreviewMode]);
|
||||
};
|
||||
|
||||
const DesignMode = () => {
|
||||
@@ -215,11 +210,7 @@ const ContentEditMode = () => {
|
||||
return null;
|
||||
};
|
||||
|
||||
type CanvasProps = {
|
||||
params: Params;
|
||||
};
|
||||
|
||||
export const Canvas = ({ params }: CanvasProps) => {
|
||||
export const Canvas = () => {
|
||||
useCanvasStore();
|
||||
const isDesignMode = useStore($isDesignMode);
|
||||
const isContentMode = useStore($isContentMode);
|
||||
@@ -254,11 +245,6 @@ export const Canvas = ({ params }: CanvasProps) => {
|
||||
});
|
||||
});
|
||||
|
||||
useMount(() => {
|
||||
// required to compute asset and page props for rendering
|
||||
$params.set(params);
|
||||
});
|
||||
|
||||
useMount(initCanvasApi);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
@@ -299,7 +285,7 @@ export const Canvas = ({ params }: CanvasProps) => {
|
||||
|
||||
const components = useStore($registeredComponents);
|
||||
const instances = useStore($instances);
|
||||
const elements = useElementsTree(components, instances, params);
|
||||
const elements = useElementsTree(components, instances);
|
||||
|
||||
const [isInitialized, setInitialized] = useState(false);
|
||||
useEffect(() => {
|
||||
@@ -312,7 +298,7 @@ export const Canvas = ({ params }: CanvasProps) => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<GlobalStyles params={params} />
|
||||
<GlobalStyles />
|
||||
{/* catch all errors in rendered components */}
|
||||
<ErrorBoundary FallbackComponent={FallbackComponent}>
|
||||
{elements}
|
||||
|
||||
@@ -14,7 +14,6 @@ import {
|
||||
addGlobalRules,
|
||||
createImageValueTransformer,
|
||||
descendantComponent,
|
||||
type Params,
|
||||
rootComponent,
|
||||
} from "@webstudio-is/react-sdk";
|
||||
import {
|
||||
@@ -33,9 +32,10 @@ import {
|
||||
$selectedStyleState,
|
||||
$styleSourceSelections,
|
||||
$styles,
|
||||
assetBaseUrl,
|
||||
} from "~/shared/nano-states";
|
||||
import { setDifference } from "~/shared/shim";
|
||||
import { $ephemeralStyles, $params } from "../stores";
|
||||
import { $ephemeralStyles } from "../stores";
|
||||
import { canvasApi } from "~/shared/canvas-api";
|
||||
import { $selectedInstance, $selectedPage } from "~/shared/awareness";
|
||||
import { findAllEditableInstanceSelector } from "~/shared/instance-utils";
|
||||
@@ -225,9 +225,9 @@ const subscribeContentEditModeHelperStyles = () => {
|
||||
|
||||
// keep stable transformValue in store
|
||||
// to preserve cache in css engine
|
||||
const $transformValue = computed([$assets, $params], (assets, params) =>
|
||||
const $transformValue = computed($assets, (assets) =>
|
||||
createImageValueTransformer(assets, {
|
||||
assetBaseUrl: params?.assetBaseUrl ?? "",
|
||||
assetBaseUrl,
|
||||
})
|
||||
);
|
||||
|
||||
@@ -428,7 +428,7 @@ export const manageDesignModeStyles = ({ signal }: { signal: AbortSignal }) => {
|
||||
});
|
||||
};
|
||||
|
||||
export const GlobalStyles = ({ params }: { params: Params }) => {
|
||||
export const GlobalStyles = () => {
|
||||
const assets = useStore($assets);
|
||||
const metas = useStore($registeredComponentMetas);
|
||||
|
||||
@@ -436,10 +436,10 @@ export const GlobalStyles = ({ params }: { params: Params }) => {
|
||||
fontsAndDefaultsSheet.clear();
|
||||
addGlobalRules(fontsAndDefaultsSheet, {
|
||||
assets,
|
||||
assetBaseUrl: params.assetBaseUrl,
|
||||
assetBaseUrl,
|
||||
});
|
||||
fontsAndDefaultsSheet.render();
|
||||
}, [assets, params.assetBaseUrl]);
|
||||
}, [assets]);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
presetSheet.clear();
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
import { atom } from "nanostores";
|
||||
import type { StyleDecl } from "@webstudio-is/sdk";
|
||||
import type { Params } from "@webstudio-is/react-sdk";
|
||||
|
||||
export const $ephemeralStyles = atom<Array<StyleDecl>>([]);
|
||||
|
||||
export const $params = atom<undefined | Params>();
|
||||
Vendored
-16
@@ -23,22 +23,6 @@ const env = {
|
||||
// Assets
|
||||
MAX_UPLOAD_SIZE: process.env.MAX_UPLOAD_SIZE,
|
||||
MAX_ASSETS_PER_PROJECT: process.env.MAX_ASSETS_PER_PROJECT,
|
||||
/**
|
||||
* Base url or base path for any asset with ending slash.
|
||||
* Possible values are
|
||||
* /s/uploads/
|
||||
* /cgi/asset/
|
||||
* https://assets-dev.webstudio.is/
|
||||
* https://assets.webstudio.is/
|
||||
*/
|
||||
ASSET_BASE_URL:
|
||||
process.env.ASSET_BASE_URL ??
|
||||
process.env.ASSET_CDN_URL ??
|
||||
process.env.ASSET_PUBLIC_PATH ??
|
||||
"/cgi/asset/",
|
||||
|
||||
// Local assets
|
||||
FILE_UPLOAD_PATH: process.env.FILE_UPLOAD_PATH,
|
||||
|
||||
// Remote assets
|
||||
S3_ENDPOINT: process.env.S3_ENDPOINT,
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
import { lazy } from "react";
|
||||
import { type LoaderFunctionArgs } from "@remix-run/server-runtime";
|
||||
import { useLoaderData } from "@remix-run/react";
|
||||
import type { Params } from "@webstudio-is/react-sdk";
|
||||
import { Body } from "@webstudio-is/sdk-components-react-remix";
|
||||
import env from "~/env/env.server";
|
||||
import { isCanvas } from "~/shared/router-utils";
|
||||
import { ClientOnly } from "~/shared/client-only";
|
||||
export { ErrorBoundary } from "~/shared/error/error-boundary";
|
||||
@@ -14,12 +11,7 @@ export const loader = async ({ request }: LoaderFunctionArgs) => {
|
||||
status: 404,
|
||||
});
|
||||
}
|
||||
|
||||
const params: Params = {
|
||||
assetBaseUrl: env.ASSET_BASE_URL,
|
||||
};
|
||||
|
||||
return { params };
|
||||
return {};
|
||||
};
|
||||
|
||||
const Canvas = lazy(async () => {
|
||||
@@ -28,10 +20,9 @@ const Canvas = lazy(async () => {
|
||||
});
|
||||
|
||||
const CanvasRoute = () => {
|
||||
const { params } = useLoaderData<typeof loader>();
|
||||
return (
|
||||
<ClientOnly fallback={<Body />}>
|
||||
<Canvas params={params} />
|
||||
<Canvas />
|
||||
</ClientOnly>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -6,6 +6,7 @@ import type { LoaderFunctionArgs } from "@remix-run/server-runtime";
|
||||
import { wsImageLoader } from "@webstudio-is/image";
|
||||
import env from "~/env/env.server";
|
||||
import { getImageNameAndType } from "~/builder/shared/assets/asset-utils";
|
||||
import { fileUploadPath } from "~/shared/asset-client";
|
||||
|
||||
const ImageParams = z.object({
|
||||
width: z.string().transform((value) => Math.round(parseFloat(value))),
|
||||
@@ -101,14 +102,10 @@ export const loader = async ({ request }: LoaderFunctionArgs) => {
|
||||
return responseWHeaders;
|
||||
}
|
||||
|
||||
if (env.FILE_UPLOAD_PATH === undefined) {
|
||||
throw Error("FILE_UPLOAD_PATH is not provided");
|
||||
}
|
||||
// support absolute urls locally
|
||||
if (URL.canParse(name)) {
|
||||
return fetch(name);
|
||||
}
|
||||
const fileUploadPath = env.FILE_UPLOAD_PATH;
|
||||
const filePath = join(process.cwd(), fileUploadPath, name);
|
||||
|
||||
if (existsSync(filePath) === false) {
|
||||
|
||||
@@ -6,6 +6,8 @@ import {
|
||||
} from "@webstudio-is/asset-uploader/index.server";
|
||||
import env from "~/env/env.server";
|
||||
|
||||
export const fileUploadPath = "public/cgi/asset";
|
||||
|
||||
export const createAssetClient = () => {
|
||||
const maxUploadSize = MaxSize.parse(env.MAX_UPLOAD_SIZE);
|
||||
if (
|
||||
@@ -25,7 +27,6 @@ export const createAssetClient = () => {
|
||||
maxUploadSize,
|
||||
});
|
||||
} else {
|
||||
const fileUploadPath = env.FILE_UPLOAD_PATH ?? "public/s/uploads";
|
||||
return createFsClient({
|
||||
maxUploadSize,
|
||||
fileDirectory: path.join(process.cwd(), fileUploadPath),
|
||||
|
||||
@@ -15,7 +15,6 @@ import {
|
||||
} from "./props";
|
||||
import { $pages } from "./pages";
|
||||
import { $assets, $dataSources, $props, $resources } from "./misc";
|
||||
import { $params } from "~/canvas/stores";
|
||||
import { $dataSourceVariables, $resourceValues } from "./variables";
|
||||
import { $awareness } from "../awareness";
|
||||
|
||||
@@ -206,11 +205,10 @@ test("generate action prop callbacks", () => {
|
||||
cleanStores($propValuesByInstanceSelector);
|
||||
});
|
||||
|
||||
test("resolve asset prop values when params is provided", () => {
|
||||
test("resolve asset prop values", () => {
|
||||
setBoxInstance("box");
|
||||
selectPageRoot("box");
|
||||
$dataSources.set(new Map());
|
||||
$params.set(undefined);
|
||||
$assets.set(
|
||||
toMap([
|
||||
{
|
||||
@@ -237,29 +235,21 @@ test("resolve asset prop values when params is provided", () => {
|
||||
},
|
||||
])
|
||||
);
|
||||
expect(
|
||||
$propValuesByInstanceSelector.get().get(JSON.stringify(["box"]))
|
||||
).toEqual(new Map());
|
||||
|
||||
$params.set({
|
||||
assetBaseUrl: "/asset/",
|
||||
});
|
||||
expect(
|
||||
$propValuesByInstanceSelector.get().get(JSON.stringify(["box"]))
|
||||
).toEqual(
|
||||
new Map<string, unknown>([
|
||||
["$webstudio$canvasOnly$assetId", "assetId"],
|
||||
["myAsset", "/asset/my-file.jpg"],
|
||||
["myAsset", "/cgi/asset/my-file.jpg"],
|
||||
])
|
||||
);
|
||||
|
||||
cleanStores($propValuesByInstanceSelector);
|
||||
});
|
||||
|
||||
test("resolve page prop values when params is provided", () => {
|
||||
test("resolve page prop values", () => {
|
||||
setBoxInstance("box");
|
||||
selectPageRoot("box");
|
||||
$params.set(undefined);
|
||||
$dataSources.set(new Map());
|
||||
$props.set(
|
||||
toMap([
|
||||
@@ -272,13 +262,6 @@ test("resolve page prop values when params is provided", () => {
|
||||
},
|
||||
])
|
||||
);
|
||||
expect(
|
||||
$propValuesByInstanceSelector.get().get(JSON.stringify(["box"]))
|
||||
).toEqual(new Map());
|
||||
|
||||
$params.set({
|
||||
assetBaseUrl: "/asset/",
|
||||
});
|
||||
expect(
|
||||
$propValuesByInstanceSelector.get().get(JSON.stringify(["box"]))
|
||||
).toEqual(new Map<string, unknown>([["myPage", "/"]]));
|
||||
|
||||
@@ -37,7 +37,6 @@ import {
|
||||
} from "./misc";
|
||||
import { $pages } from "./pages";
|
||||
import type { InstanceSelector } from "../tree-utils";
|
||||
import { $params } from "~/canvas/stores";
|
||||
import { restResourcesLoader } from "../router-utils";
|
||||
import {
|
||||
$dataSourceVariables,
|
||||
@@ -49,6 +48,8 @@ import { uploadingFileDataToAsset } from "~/builder/shared/assets/asset-utils";
|
||||
import { fetch } from "~/shared/fetch.client";
|
||||
import { $selectedPage, getInstanceKey } from "../awareness";
|
||||
|
||||
export const assetBaseUrl = "/cgi/asset/";
|
||||
|
||||
export const getIndexedInstanceId = (
|
||||
instanceId: Instance["id"],
|
||||
index: number
|
||||
@@ -291,7 +292,6 @@ export const $propValuesByInstanceSelector = computed(
|
||||
$props,
|
||||
$selectedPage,
|
||||
$unscopedVariableValues,
|
||||
$params,
|
||||
$pages,
|
||||
$assets,
|
||||
$uploadingFilesDataStore,
|
||||
@@ -301,7 +301,6 @@ export const $propValuesByInstanceSelector = computed(
|
||||
props,
|
||||
page,
|
||||
unscopedVariableValues,
|
||||
params,
|
||||
pages,
|
||||
assets,
|
||||
uploadingFilesDataStore
|
||||
@@ -311,7 +310,7 @@ export const $propValuesByInstanceSelector = computed(
|
||||
let propsList = Array.from(props.values());
|
||||
|
||||
// ignore asset and page props when params is not provided
|
||||
if (params && pages) {
|
||||
if (pages) {
|
||||
const uploadingImageAssets = uploadingFilesDataStore
|
||||
.map(uploadingFileDataToAsset)
|
||||
.filter(<T>(value: T): value is NonNullable<T> => value !== undefined)
|
||||
@@ -320,7 +319,7 @@ export const $propValuesByInstanceSelector = computed(
|
||||
// use whole props list to let access hash props from other pages and instances
|
||||
propsList = normalizeProps({
|
||||
props: propsList,
|
||||
assetBaseUrl: params.assetBaseUrl,
|
||||
assetBaseUrl,
|
||||
assets,
|
||||
uploadingImageAssets,
|
||||
pages,
|
||||
|
||||
Reference in new issue
Block a user