From a4ee9beeb225398c8b115b86a6d2b3eef93f0b4a Mon Sep 17 00:00:00 2001 From: Sara Vieira Date: Tue, 12 Jul 2022 15:06:41 +0200 Subject: [PATCH 01/20] add sidebar for image-upload --- .../features/sidebar-left/panels/image-upload.tsx | 15 +++++++++++++++ .../features/sidebar-left/panels/index.ts | 1 + 2 files changed, 16 insertions(+) create mode 100644 apps/designer/app/designer/features/sidebar-left/panels/image-upload.tsx diff --git a/apps/designer/app/designer/features/sidebar-left/panels/image-upload.tsx b/apps/designer/app/designer/features/sidebar-left/panels/image-upload.tsx new file mode 100644 index 0000000000..706636c631 --- /dev/null +++ b/apps/designer/app/designer/features/sidebar-left/panels/image-upload.tsx @@ -0,0 +1,15 @@ +import { ImageIcon } from "~/shared/icons"; +import { Button, Flex, Heading } from "~/shared/design-system"; + +export const TabContent = () => { + return ( + + Assets + + + + + ); +}; + +export const icon = ; diff --git a/apps/designer/app/designer/features/sidebar-left/panels/index.ts b/apps/designer/app/designer/features/sidebar-left/panels/index.ts index 589445e67f..83a3e7fda8 100644 --- a/apps/designer/app/designer/features/sidebar-left/panels/index.ts +++ b/apps/designer/app/designer/features/sidebar-left/panels/index.ts @@ -1,2 +1,3 @@ export * as components from "./components"; export * as tree from "./tree"; +export * as imageUpload from "./image-upload"; From d9c9b167618c42ef241e0bfe9a6373bc1ae9e5f2 Mon Sep 17 00:00:00 2001 From: Sara Vieira Date: Tue, 12 Jul 2022 15:07:34 +0200 Subject: [PATCH 02/20] add starter flag --- apps/designer/app/shared/feature-flags/flags.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/designer/app/shared/feature-flags/flags.ts b/apps/designer/app/shared/feature-flags/flags.ts index fe68be041d..00b857f82c 100644 --- a/apps/designer/app/shared/feature-flags/flags.ts +++ b/apps/designer/app/shared/feature-flags/flags.ts @@ -1 +1,2 @@ export const theme = false; +export const assets = false; From b620c45351ca82ab82abb410dbd01a47d46c0118 Mon Sep 17 00:00:00 2001 From: Sara Vieira Date: Thu, 14 Jul 2022 13:59:14 +0200 Subject: [PATCH 03/20] make local uploads work --- .gitignore | 1 + apps/designer/app/designer/designer.tsx | 10 ++++- .../sidebar-left/panels/image-upload.tsx | 42 ++++++++++++++++--- .../features/sidebar-left/sidebar-left.tsx | 9 +++- apps/designer/app/routes/designer/$id.tsx | 34 ++++++++++++++- apps/designer/app/shared/db/assets.server.ts | 28 +++++++++++++ apps/designer/app/shared/db/index.ts | 1 + apps/designer/package.json | 1 + yarn.lock | 36 ++++++++++++++-- 9 files changed, 149 insertions(+), 13 deletions(-) create mode 100644 apps/designer/app/shared/db/assets.server.ts diff --git a/.gitignore b/.gitignore index 6391b4dd9c..b74e9a335a 100644 --- a/.gitignore +++ b/.gitignore @@ -40,3 +40,4 @@ yarn-error.log* /data *.db *.db-journal +uploads diff --git a/apps/designer/app/designer/designer.tsx b/apps/designer/app/designer/designer.tsx index 9be7dbb5e3..d65a67a370 100644 --- a/apps/designer/app/designer/designer.tsx +++ b/apps/designer/app/designer/designer.tsx @@ -33,6 +33,7 @@ import { usePublishShortcuts } from "./shared/shortcuts"; import { type SyncStatus } from "~/shared/sync"; import { useIsPreviewMode, useRootInstance } from "~/shared/nano-states"; import { useSubscribeClientSetting } from "./shared/client-settings"; +import { Asset } from "@prisma/client"; export const links = () => { return [ @@ -170,9 +171,10 @@ const ChromeWrapper = ({ children, isPreviewMode }: ChromeWrapperProps) => { type DesignerProps = { config: Config; project: Project; + assets: Array; }; -export const Designer = ({ config, project }: DesignerProps) => { +export const Designer = ({ config, project, assets }: DesignerProps) => { useSubscribeSyncStatus(); useSubscribeRootInstance(); useSubscribeSelectedInstanceData(); @@ -219,7 +221,11 @@ export const Designer = ({ config, project }: DesignerProps) => { - + { +export const TabContent = ({ assets }: { assets: Array }) => { + const inputRef = useRef(); + const formRef = useRef(); + console.log(assets); return ( - Assets - - + + Assets +
+ formRef.current && formRef.current.submit()} + style={{ display: "none" }} + /> + +
+ + {assets.map((asset) => ( + {asset.alt + ))} +
); }; diff --git a/apps/designer/app/designer/features/sidebar-left/sidebar-left.tsx b/apps/designer/app/designer/features/sidebar-left/sidebar-left.tsx index dd17e0af18..9a2f3a20f1 100644 --- a/apps/designer/app/designer/features/sidebar-left/sidebar-left.tsx +++ b/apps/designer/app/designer/features/sidebar-left/sidebar-left.tsx @@ -10,6 +10,7 @@ import { import { useSelectedInstanceData } from "../../shared/nano-states"; import * as panels from "./panels"; import type { TabName } from "./types"; +import { Asset } from "@prisma/client"; const sidebarTabsContentStyle = { position: "absolute", @@ -26,9 +27,14 @@ const none = { TabContent: () => null }; type SidebarLeftProps = { onDragChange: (isDragging: boolean) => void; publish: Publish; + assets: Array; }; -export const SidebarLeft = ({ onDragChange, publish }: SidebarLeftProps) => { +export const SidebarLeft = ({ + onDragChange, + publish, + assets, +}: SidebarLeftProps) => { const [selectedInstanceData] = useSelectedInstanceData(); const [activeTab, setActiveTab] = useState("none"); const [isDragging, setIsDragging] = useState(false); @@ -83,6 +89,7 @@ export const SidebarLeft = ({ onDragChange, publish }: SidebarLeftProps) => { }} > { if (params.id === undefined) throw new Error("Project id undefined"); const project = await db.project.loadById(params.id); + const assets = await db.assets.loadByProject(params.id); if (project === null) { return { errors: `Project "${params.id}" not found` }; } - return { config, project, env }; + return { config, assets, project, env }; }; +const directory = "./public/uploads"; + type Data = { config: typeof config; project: Project; @@ -25,6 +33,28 @@ type Error = { errors: "string"; }; +export const action: ActionFunction = async ({ request, params }) => { + if (params.id === undefined) throw new Error("Project id undefined"); + const formData = await unstable_parseMultipartFormData( + request, + unstable_createFileUploadHandler({ + maxPartSize: 5_000_000, + directory, + file: ({ filename }) => filename, + }) + ); + const imageInfo = formData.get("image"); + if (imageInfo) { + const data = { + type: imageInfo.type, + name: imageInfo.name, + path: `/uploads/${imageInfo.name}`, + }; + db.assets.create(params.id, data); + } + return "lol"; +}; + const DesignerRoute = () => { const data = useLoaderData(); if ("errors" in data) { diff --git a/apps/designer/app/shared/db/assets.server.ts b/apps/designer/app/shared/db/assets.server.ts new file mode 100644 index 0000000000..79373c0026 --- /dev/null +++ b/apps/designer/app/shared/db/assets.server.ts @@ -0,0 +1,28 @@ +import { type Project } from "@webstudio-is/sdk"; +import { prisma } from "./prisma.server"; + +export const loadByProject = async (projectId?: Project["id"]) => { + if (typeof projectId !== "string") { + throw new Error("Tree ID required"); + } + + const assets = await prisma.asset.findMany({ + where: { projectId }, + }); + + return assets; +}; + +export const create = async ( + projectId: Project["id"], + values: { type: string; name: string; path: string } +) => { + const newAsset = await prisma.asset.create({ + data: { + ...values, + projectId, + }, + }); + + return newAsset; +}; diff --git a/apps/designer/app/shared/db/index.ts b/apps/designer/app/shared/db/index.ts index 8e5524eaa7..34e3de24f5 100644 --- a/apps/designer/app/shared/db/index.ts +++ b/apps/designer/app/shared/db/index.ts @@ -5,3 +5,4 @@ export * as misc from "./misc.server"; export * as user from "./user.server"; export * from "./canvas.server"; export * as breakpoints from "./breakpoints.server"; +export * as assets from "./assets.server"; diff --git a/apps/designer/package.json b/apps/designer/package.json index b18f8a14ed..d36be25a3d 100644 --- a/apps/designer/package.json +++ b/apps/designer/package.json @@ -70,6 +70,7 @@ "lodash.noop": "^3.0.1", "lodash.snakecase": "^4.1.1", "mitt": "^3.0.0", + "multer": "^1.4.5-lts.1", "nanoid": "^3.2.0", "react": "^17.0.2", "react-color": "^2.19.3", diff --git a/yarn.lock b/yarn.lock index 10cc41d6a9..95de15bdac 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4856,6 +4856,11 @@ app-root-dir@^1.0.2: resolved "https://registry.yarnpkg.com/app-root-dir/-/app-root-dir-1.0.2.tgz#38187ec2dea7577fff033ffcb12172692ff6e118" integrity sha512-jlpIfsOoNoafl92Sz//64uQHGSyMrD2vYG5d8o2a4qGvyNCvXur7bzIsWtAC/6flI2RYAp3kv8rsfBtaLm7w0g== +append-field@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/append-field/-/append-field-1.0.0.tgz#1e3440e915f0b1203d23748e78edd7b9b5b43e56" + integrity sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw== + aproba@^1.0.3, aproba@^1.1.1: version "1.2.0" resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" @@ -5628,6 +5633,13 @@ builtin-status-codes@^3.0.0: resolved "https://registry.yarnpkg.com/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz#85982878e21b98e1c66425e03d0174788f569ee8" integrity sha512-HpGFw18DgFWlncDfjTa2rcQ4W88O1mC8e8yZ2AvQY5KDaktSTwo+KRf6nHK6FRI5FyRyb/5T6+TSxfP7QyGsmQ== +busboy@^1.0.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/busboy/-/busboy-1.6.0.tgz#966ea36a9502e43cdb9146962523b92f531f6893" + integrity sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA== + dependencies: + streamsearch "^1.1.0" + bytes@3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048" @@ -6245,7 +6257,7 @@ concat-map@0.0.1: resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== -concat-stream@^1.5.0: +concat-stream@^1.5.0, concat-stream@^1.5.2: version "1.6.2" resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34" integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw== @@ -11746,7 +11758,7 @@ mkdirp-classic@^0.5.2: resolved "https://registry.yarnpkg.com/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz#fa10c9115cc6d8865be221ba47ee9bed78601113" integrity sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A== -mkdirp@^0.5.1, mkdirp@^0.5.3, mkdirp@^0.5.5: +mkdirp@^0.5.1, mkdirp@^0.5.3, mkdirp@^0.5.4, mkdirp@^0.5.5: version "0.5.6" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.6.tgz#7def03d2432dcae4ba1d611445c48396062255f6" integrity sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw== @@ -11811,6 +11823,19 @@ ms@2.1.3, ms@^2.1.1: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== +multer@^1.4.5-lts.1: + version "1.4.5-lts.1" + resolved "https://registry.yarnpkg.com/multer/-/multer-1.4.5-lts.1.tgz#803e24ad1984f58edffbc79f56e305aec5cfd1ac" + integrity sha512-ywPWvcDMeH+z9gQq5qYHCCy+ethsk4goepZ45GLD63fOu0YcNecQxi64nDs3qluZB+murG3/D4dJ7+dGctcCQQ== + dependencies: + append-field "^1.0.0" + busboy "^1.0.0" + concat-stream "^1.5.2" + mkdirp "^0.5.4" + object-assign "^4.1.1" + type-is "^1.6.4" + xtend "^4.0.0" + mute-stream@0.0.8: version "0.0.8" resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d" @@ -14635,6 +14660,11 @@ stream-slice@^0.1.2: resolved "https://registry.yarnpkg.com/stream-slice/-/stream-slice-0.1.2.tgz#2dc4f4e1b936fb13f3eb39a2def1932798d07a4b" integrity sha512-QzQxpoacatkreL6jsxnVb7X5R/pGw9OUv2qWTYWnmLpg4NdN31snPy/f3TdQE1ZUXaThRvj1Zw4/OGg0ZkaLMA== +streamsearch@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/streamsearch/-/streamsearch-1.1.0.tgz#404dd1e2247ca94af554e841a8ef0eaa238da764" + integrity sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg== + string-argv@^0.3.1: version "0.3.1" resolved "https://registry.yarnpkg.com/string-argv/-/string-argv-0.3.1.tgz#95e2fbec0427ae19184935f816d74aaa4c5c19da" @@ -15413,7 +15443,7 @@ type-fest@^0.8.1: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== -type-is@~1.6.18: +type-is@^1.6.4, type-is@~1.6.18: version "1.6.18" resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g== From ec583174a387c13f6018e9d2329e1550cefca7f0 Mon Sep 17 00:00:00 2001 From: Sara Vieira Date: Fri, 15 Jul 2022 20:20:00 +0200 Subject: [PATCH 04/20] add schema --- .../20220714112221_add_assets/migration.sql | 15 +++++++++++++++ .../20220714114102_remove_size/migration.sql | 8 ++++++++ apps/designer/prisma/schema.prisma | 11 +++++++++++ packages/webstudio-react-sdk/prisma/schema.prisma | 11 +++++++++++ 4 files changed, 45 insertions(+) create mode 100644 apps/designer/prisma/migrations/20220714112221_add_assets/migration.sql create mode 100644 apps/designer/prisma/migrations/20220714114102_remove_size/migration.sql diff --git a/apps/designer/prisma/migrations/20220714112221_add_assets/migration.sql b/apps/designer/prisma/migrations/20220714112221_add_assets/migration.sql new file mode 100644 index 0000000000..24b3a97e3c --- /dev/null +++ b/apps/designer/prisma/migrations/20220714112221_add_assets/migration.sql @@ -0,0 +1,15 @@ +-- CreateTable +CREATE TABLE "Asset" ( + "id" TEXT NOT NULL, + "projectId" TEXT NOT NULL, + "type" TEXT NOT NULL, + "name" TEXT NOT NULL, + "path" TEXT NOT NULL, + "size" DOUBLE PRECISION NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT "Asset_pkey" PRIMARY KEY ("id") +); + +-- AddForeignKey +ALTER TABLE "Asset" ADD CONSTRAINT "Asset_projectId_fkey" FOREIGN KEY ("projectId") REFERENCES "Project"("id") ON DELETE RESTRICT ON UPDATE CASCADE; diff --git a/apps/designer/prisma/migrations/20220714114102_remove_size/migration.sql b/apps/designer/prisma/migrations/20220714114102_remove_size/migration.sql new file mode 100644 index 0000000000..edc101b235 --- /dev/null +++ b/apps/designer/prisma/migrations/20220714114102_remove_size/migration.sql @@ -0,0 +1,8 @@ +/* + Warnings: + + - You are about to drop the column `size` on the `Asset` table. All the data in the column will be lost. + +*/ +-- AlterTable +ALTER TABLE "Asset" DROP COLUMN "size"; diff --git a/apps/designer/prisma/schema.prisma b/apps/designer/prisma/schema.prisma index c7a5edb888..26015a5835 100644 --- a/apps/designer/prisma/schema.prisma +++ b/apps/designer/prisma/schema.prisma @@ -13,7 +13,17 @@ datasource db { model Team { id String @id @default(uuid()) users User[] +} +model Asset { + id String @id @default(uuid()) + project Project @relation(fields: [projectId], references: [id]) + projectId String + type String + name String + path String + alt String? + createdAt DateTime @default(now()) } model User { @@ -37,6 +47,7 @@ model Project { prodTreeIdHistory String @default("[]") User User? @relation(fields: [userId], references: [id]) userId String? + Asset Asset[] } model Tree { diff --git a/packages/webstudio-react-sdk/prisma/schema.prisma b/packages/webstudio-react-sdk/prisma/schema.prisma index c7a5edb888..26015a5835 100644 --- a/packages/webstudio-react-sdk/prisma/schema.prisma +++ b/packages/webstudio-react-sdk/prisma/schema.prisma @@ -13,7 +13,17 @@ datasource db { model Team { id String @id @default(uuid()) users User[] +} +model Asset { + id String @id @default(uuid()) + project Project @relation(fields: [projectId], references: [id]) + projectId String + type String + name String + path String + alt String? + createdAt DateTime @default(now()) } model User { @@ -37,6 +47,7 @@ model Project { prodTreeIdHistory String @default("[]") User User? @relation(fields: [userId], references: [id]) userId String? + Asset Asset[] } model Tree { From d6bb48c975789e1ae28271264a071bce05482872 Mon Sep 17 00:00:00 2001 From: Sara Vieira Date: Fri, 15 Jul 2022 22:13:45 +0200 Subject: [PATCH 05/20] fix tab order; fix imports --- .../sidebar-left/panels/image-upload.tsx | 23 ++++++++++++++----- .../features/sidebar-left/panels/index.ts | 8 ++++--- .../features/sidebar-left/sidebar-left.tsx | 11 +++++++-- apps/designer/app/env.server.ts | 2 +- apps/designer/app/routes/designer/$id.tsx | 11 +++++++-- apps/designer/app/shared/db/assets.server.ts | 2 +- .../app/shared/feature-flags/feature.ts | 8 +++---- .../app/shared/feature-flags/flags.ts | 1 - .../20220715192633_add_alt/migration.sql | 2 ++ 9 files changed, 48 insertions(+), 20 deletions(-) create mode 100644 apps/designer/prisma/migrations/20220715192633_add_alt/migration.sql diff --git a/apps/designer/app/designer/features/sidebar-left/panels/image-upload.tsx b/apps/designer/app/designer/features/sidebar-left/panels/image-upload.tsx index e1a016dc45..e94fdeadb2 100644 --- a/apps/designer/app/designer/features/sidebar-left/panels/image-upload.tsx +++ b/apps/designer/app/designer/features/sidebar-left/panels/image-upload.tsx @@ -1,14 +1,20 @@ import { ImageIcon } from "~/shared/icons"; import { Button, Flex, Grid, Heading } from "~/shared/design-system"; -import { useRef } from "react"; +import React, { useRef } from "react"; import { Form } from "@remix-run/react"; import { Asset } from "@prisma/client"; import { Image } from "~/shared/design-system/components/image"; export const TabContent = ({ assets }: { assets: Array }) => { - const inputRef = useRef(); - const formRef = useRef(); - console.log(assets); + const inputRef = useRef(null); + const submitButtonRef = useRef(null); + + const onChange = () => { + if (submitButtonRef.current) { + submitButtonRef.current.click(); + } + }; + return ( }) => { }} > Assets -
+ formRef.current && formRef.current.submit()} + onChange={onChange} + style={{ display: "none" }} + /> +
- + {assets.map((asset) => ( { }; db.assets.create(params.id, data); } - return "lol"; + return { + ok: true, + }; }; const DesignerRoute = () => { diff --git a/apps/designer/package.json b/apps/designer/package.json index 539c11aefa..6c064b6831 100644 --- a/apps/designer/package.json +++ b/apps/designer/package.json @@ -70,7 +70,6 @@ "lodash.noop": "^3.0.1", "lodash.snakecase": "^4.1.1", "mitt": "^3.0.0", - "multer": "^1.4.5-lts.1", "nanoid": "^3.2.0", "react": "^17.0.2", "react-color": "^2.19.3", diff --git a/yarn.lock b/yarn.lock index 222af37d3b..2ed1f4074b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4866,11 +4866,6 @@ app-root-dir@^1.0.2: resolved "https://registry.yarnpkg.com/app-root-dir/-/app-root-dir-1.0.2.tgz#38187ec2dea7577fff033ffcb12172692ff6e118" integrity sha512-jlpIfsOoNoafl92Sz//64uQHGSyMrD2vYG5d8o2a4qGvyNCvXur7bzIsWtAC/6flI2RYAp3kv8rsfBtaLm7w0g== -append-field@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/append-field/-/append-field-1.0.0.tgz#1e3440e915f0b1203d23748e78edd7b9b5b43e56" - integrity sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw== - aproba@^1.0.3, aproba@^1.1.1: version "1.2.0" resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" @@ -5643,13 +5638,6 @@ builtin-status-codes@^3.0.0: resolved "https://registry.yarnpkg.com/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz#85982878e21b98e1c66425e03d0174788f569ee8" integrity sha512-HpGFw18DgFWlncDfjTa2rcQ4W88O1mC8e8yZ2AvQY5KDaktSTwo+KRf6nHK6FRI5FyRyb/5T6+TSxfP7QyGsmQ== -busboy@^1.0.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/busboy/-/busboy-1.6.0.tgz#966ea36a9502e43cdb9146962523b92f531f6893" - integrity sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA== - dependencies: - streamsearch "^1.1.0" - bytes@3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048" @@ -6267,7 +6255,7 @@ concat-map@0.0.1: resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== -concat-stream@^1.5.0, concat-stream@^1.5.2: +concat-stream@^1.5.0: version "1.6.2" resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34" integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw== @@ -11899,7 +11887,7 @@ mkdirp-classic@^0.5.2: resolved "https://registry.yarnpkg.com/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz#fa10c9115cc6d8865be221ba47ee9bed78601113" integrity sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A== -mkdirp@^0.5.1, mkdirp@^0.5.3, mkdirp@^0.5.4, mkdirp@^0.5.5: +mkdirp@^0.5.1, mkdirp@^0.5.3, mkdirp@^0.5.5: version "0.5.6" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.6.tgz#7def03d2432dcae4ba1d611445c48396062255f6" integrity sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw== @@ -11964,19 +11952,6 @@ ms@2.1.3, ms@^2.1.1: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== -multer@^1.4.5-lts.1: - version "1.4.5-lts.1" - resolved "https://registry.yarnpkg.com/multer/-/multer-1.4.5-lts.1.tgz#803e24ad1984f58edffbc79f56e305aec5cfd1ac" - integrity sha512-ywPWvcDMeH+z9gQq5qYHCCy+ethsk4goepZ45GLD63fOu0YcNecQxi64nDs3qluZB+murG3/D4dJ7+dGctcCQQ== - dependencies: - append-field "^1.0.0" - busboy "^1.0.0" - concat-stream "^1.5.2" - mkdirp "^0.5.4" - object-assign "^4.1.1" - type-is "^1.6.4" - xtend "^4.0.0" - mute-stream@0.0.8: version "0.0.8" resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d" @@ -14801,11 +14776,6 @@ stream-slice@^0.1.2: resolved "https://registry.yarnpkg.com/stream-slice/-/stream-slice-0.1.2.tgz#2dc4f4e1b936fb13f3eb39a2def1932798d07a4b" integrity sha512-QzQxpoacatkreL6jsxnVb7X5R/pGw9OUv2qWTYWnmLpg4NdN31snPy/f3TdQE1ZUXaThRvj1Zw4/OGg0ZkaLMA== -streamsearch@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/streamsearch/-/streamsearch-1.1.0.tgz#404dd1e2247ca94af554e841a8ef0eaa238da764" - integrity sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg== - string-argv@^0.3.1: version "0.3.1" resolved "https://registry.yarnpkg.com/string-argv/-/string-argv-0.3.1.tgz#95e2fbec0427ae19184935f816d74aaa4c5c19da" @@ -15584,7 +15554,7 @@ type-fest@^0.8.1: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== -type-is@^1.6.4, type-is@~1.6.18: +type-is@~1.6.18: version "1.6.18" resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g== From f3611d61843976dde1ae2da71fcff481caee68ce Mon Sep 17 00:00:00 2001 From: Sara Vieira Date: Fri, 15 Jul 2022 22:26:21 +0200 Subject: [PATCH 08/20] remove unsuded --- .../app/designer/features/sidebar-left/panels/image-upload.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/designer/app/designer/features/sidebar-left/panels/image-upload.tsx b/apps/designer/app/designer/features/sidebar-left/panels/image-upload.tsx index fec39ef12f..22a5e68e4a 100644 --- a/apps/designer/app/designer/features/sidebar-left/panels/image-upload.tsx +++ b/apps/designer/app/designer/features/sidebar-left/panels/image-upload.tsx @@ -1,6 +1,6 @@ import { ImageIcon } from "~/shared/icons"; import { Button, Flex, Grid, Heading } from "~/shared/design-system"; -import React, { useRef } from "react"; +import { useRef } from "react"; import { Form } from "@remix-run/react"; import { Asset } from "@prisma/client"; import { Image } from "~/shared/design-system/components/image"; From 1ca940f6422ea6a1f3daa48a5d4e2cb27a649649 Mon Sep 17 00:00:00 2001 From: Sara Vieira Date: Fri, 15 Jul 2022 22:41:37 +0200 Subject: [PATCH 09/20] import asset type from SDK --- apps/designer/app/designer/designer.tsx | 2 +- .../app/designer/features/sidebar-left/panels/image-upload.tsx | 2 +- apps/designer/app/routes/designer/$id.tsx | 2 +- packages/webstudio-react-sdk/src/db/types.ts | 3 ++- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/apps/designer/app/designer/designer.tsx b/apps/designer/app/designer/designer.tsx index 2630806447..253dcfac76 100644 --- a/apps/designer/app/designer/designer.tsx +++ b/apps/designer/app/designer/designer.tsx @@ -3,6 +3,7 @@ import { DndProvider } from "react-dnd"; import { TouchBackend } from "react-dnd-touch-backend"; import { type Project, + type Asset, useSubscribe, usePublish, } from "@webstudio-is/react-sdk"; @@ -37,7 +38,6 @@ import { usePublishShortcuts } from "./shared/shortcuts"; import { type SyncStatus } from "~/shared/sync"; import { useIsPreviewMode, useRootInstance } from "~/shared/nano-states"; import { useSubscribeClientSetting } from "./shared/client-settings"; -import { Asset } from "@prisma/client"; export const links = () => { return [ diff --git a/apps/designer/app/designer/features/sidebar-left/panels/image-upload.tsx b/apps/designer/app/designer/features/sidebar-left/panels/image-upload.tsx index 22a5e68e4a..58b03d701f 100644 --- a/apps/designer/app/designer/features/sidebar-left/panels/image-upload.tsx +++ b/apps/designer/app/designer/features/sidebar-left/panels/image-upload.tsx @@ -2,7 +2,7 @@ import { ImageIcon } from "~/shared/icons"; import { Button, Flex, Grid, Heading } from "~/shared/design-system"; import { useRef } from "react"; import { Form } from "@remix-run/react"; -import { Asset } from "@prisma/client"; +import { type Asset } from "@webstudio-is/react-sdk"; import { Image } from "~/shared/design-system/components/image"; export const TabContent = ({ assets }: { assets: Array }) => { diff --git a/apps/designer/app/routes/designer/$id.tsx b/apps/designer/app/routes/designer/$id.tsx index 22ca1c313e..35f561af53 100644 --- a/apps/designer/app/routes/designer/$id.tsx +++ b/apps/designer/app/routes/designer/$id.tsx @@ -10,7 +10,7 @@ import { Designer, links } from "~/designer"; import * as db from "~/shared/db"; import config from "~/config"; import env from "~/env.server"; -import { Asset } from "@prisma/client"; +import { type Asset } from "@webstudio-is/react-sdk"; export { links }; export const loader: LoaderFunction = async ({ params }) => { diff --git a/packages/webstudio-react-sdk/src/db/types.ts b/packages/webstudio-react-sdk/src/db/types.ts index bb60157308..ec8ea7db91 100644 --- a/packages/webstudio-react-sdk/src/db/types.ts +++ b/packages/webstudio-react-sdk/src/db/types.ts @@ -3,6 +3,7 @@ import type { Project, User, Breakpoints, + Asset, } from "@prisma/client"; import type { UserProp } from "../user-props"; import type { Instance } from "./instance"; @@ -16,4 +17,4 @@ export type InstanceProps = Omit & { props: Array; }; -export type { DbInstanceProps, Project, User, Breakpoints }; +export type { DbInstanceProps, Project, User, Breakpoints, Asset }; From c29824b04a0116c0dfd95fe8742f822ae68d6ea4 Mon Sep 17 00:00:00 2001 From: Sara Vieira Date: Fri, 15 Jul 2022 22:59:15 +0200 Subject: [PATCH 10/20] Update apps/designer/app/designer/features/sidebar-left/panels/image-upload.tsx Co-authored-by: Oleg Isonen --- .../app/designer/features/sidebar-left/panels/image-upload.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/designer/app/designer/features/sidebar-left/panels/image-upload.tsx b/apps/designer/app/designer/features/sidebar-left/panels/image-upload.tsx index 58b03d701f..db40227b79 100644 --- a/apps/designer/app/designer/features/sidebar-left/panels/image-upload.tsx +++ b/apps/designer/app/designer/features/sidebar-left/panels/image-upload.tsx @@ -37,7 +37,7 @@ export const TabContent = ({ assets }: { assets: Array }) => { type="submit" style={{ display: "none" }} /> - From d1b4e9fa85b623c1f0bb6c677183eb1de94b86f8 Mon Sep 17 00:00:00 2001 From: Sara Vieira Date: Fri, 15 Jul 2022 23:00:53 +0200 Subject: [PATCH 11/20] more pr fixes --- .../designer/features/sidebar-left/sidebar-left.tsx | 11 +++++++---- apps/designer/app/routes/designer/$id.tsx | 4 ++-- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/apps/designer/app/designer/features/sidebar-left/sidebar-left.tsx b/apps/designer/app/designer/features/sidebar-left/sidebar-left.tsx index 57269236ec..34341be7c7 100644 --- a/apps/designer/app/designer/features/sidebar-left/sidebar-left.tsx +++ b/apps/designer/app/designer/features/sidebar-left/sidebar-left.tsx @@ -1,5 +1,9 @@ import { useState, useCallback } from "react"; -import { useSubscribe, type Publish } from "@webstudio-is/react-sdk"; +import { + useSubscribe, + type Publish, + type Asset, +} from "@webstudio-is/react-sdk"; import { Box, SidebarTabs, @@ -10,7 +14,6 @@ import { import { useSelectedInstanceData } from "../../shared/nano-states"; import { panels } from "./panels"; import type { TabName } from "./types"; -import { Asset } from "@prisma/client"; import { isFeatureEnabled } from "~/shared/feature-flags"; const sidebarTabsContentStyle = { @@ -62,7 +65,7 @@ export const SidebarLeft = ({ [onDragChange] ); - const panelsToMap = ( + const enabledPanels = ( isFeatureEnabled("assets") ? Object.keys(panels) : Object.keys(panels).filter((panel) => panel !== "imageUpload") @@ -72,7 +75,7 @@ export const SidebarLeft = ({ - {panelsToMap.map((tabName: TabName) => ( + {enabledPanels.map((tabName: TabName) => ( { From d7fa14b4c50869cc7bccffd6d12fee072dda7055 Mon Sep 17 00:00:00 2001 From: Sara Vieira Date: Fri, 15 Jul 2022 23:09:42 +0200 Subject: [PATCH 12/20] more pr fixes --- apps/designer/app/routes/designer/$id.tsx | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/apps/designer/app/routes/designer/$id.tsx b/apps/designer/app/routes/designer/$id.tsx index 3b91829e6f..03bcda0e65 100644 --- a/apps/designer/app/routes/designer/$id.tsx +++ b/apps/designer/app/routes/designer/$id.tsx @@ -10,7 +10,7 @@ import { Designer, links } from "~/designer"; import * as db from "~/shared/db"; import config from "~/config"; import env from "~/env.server"; - +import { z } from "zod"; export { links }; export const loader: LoaderFunction = async ({ params }) => { @@ -35,10 +35,11 @@ type Error = { errors: "string"; }; -type ImageUpload = { - type: string; - name: string; -}; +const ImageUpload = z.object({ + name: z.string(), + type: z.string(), +}); +type ImageUpload = z.infer; export const action: ActionFunction = async ({ request, params }) => { if (params.id === undefined) throw new Error("Project id undefined"); @@ -50,12 +51,15 @@ export const action: ActionFunction = async ({ request, params }) => { file: ({ filename }) => filename, }) ); - const imageInfo = formData.get("image") as ImageUpload; + const imageInfo = formData.get("image"); + ImageUpload.parse(imageInfo); + if (imageInfo) { + const info = imageInfo as ImageUpload; const data = { - type: imageInfo.type, - name: imageInfo.name, - path: `/uploads/${imageInfo.name}`, + type: info.type, + name: info.name, + path: `/uploads/${info.name}`, }; db.assets.create(params.id, data); } From 0149a1403a7059f4f15424ccb4650f5f45883547 Mon Sep 17 00:00:00 2001 From: Sara Vieira Date: Fri, 15 Jul 2022 23:16:04 +0200 Subject: [PATCH 13/20] Update apps/designer/app/routes/designer/$id.tsx Co-authored-by: Oleg Isonen --- apps/designer/app/routes/designer/$id.tsx | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/apps/designer/app/routes/designer/$id.tsx b/apps/designer/app/routes/designer/$id.tsx index 03bcda0e65..a133af2040 100644 --- a/apps/designer/app/routes/designer/$id.tsx +++ b/apps/designer/app/routes/designer/$id.tsx @@ -51,10 +51,7 @@ export const action: ActionFunction = async ({ request, params }) => { file: ({ filename }) => filename, }) ); - const imageInfo = formData.get("image"); - ImageUpload.parse(imageInfo); - - if (imageInfo) { + const imageInfo = ImageUpload.parse(formData.get("image")); const info = imageInfo as ImageUpload; const data = { type: info.type, From 1cc9b136ac689d4017a5da86b34b82f51b14331f Mon Sep 17 00:00:00 2001 From: Sara Vieira Date: Fri, 15 Jul 2022 23:22:08 +0200 Subject: [PATCH 14/20] avoid creating files with same name --- apps/designer/app/routes/designer/$id.tsx | 41 +++++++++++++++-------- 1 file changed, 27 insertions(+), 14 deletions(-) diff --git a/apps/designer/app/routes/designer/$id.tsx b/apps/designer/app/routes/designer/$id.tsx index a133af2040..2471ad868d 100644 --- a/apps/designer/app/routes/designer/$id.tsx +++ b/apps/designer/app/routes/designer/$id.tsx @@ -11,6 +11,7 @@ import * as db from "~/shared/db"; import config from "~/config"; import env from "~/env.server"; import { z } from "zod"; +import ObjectID from "bson-objectid"; export { links }; export const loader: LoaderFunction = async ({ params }) => { @@ -43,26 +44,38 @@ type ImageUpload = z.infer; export const action: ActionFunction = async ({ request, params }) => { if (params.id === undefined) throw new Error("Project id undefined"); - const formData = await unstable_parseMultipartFormData( - request, - unstable_createFileUploadHandler({ - maxPartSize: 10_000_000, - directory, - file: ({ filename }) => filename, - }) - ); - const imageInfo = ImageUpload.parse(formData.get("image")); + try { + const randomId = ObjectID.toString(); + const formData = await unstable_parseMultipartFormData( + request, + unstable_createFileUploadHandler({ + maxPartSize: 10_000_000, + directory, + file: ({ filename }) => `${randomId}_${filename}`, + }) + ); + + const imageInfo = ImageUpload.parse(formData.get("image")); + const info = imageInfo as ImageUpload; + const fileName = `${randomId}_${info.name}`; const data = { type: info.type, - name: info.name, - path: `/uploads/${info.name}`, + name: fileName, + path: `/uploads/${fileName}`, }; db.assets.create(params.id, data); + + return { + ok: true, + }; + } catch (error) { + if (error instanceof Error) { + return { + errors: error.message, + }; + } } - return { - ok: true, - }; }; const DesignerRoute = () => { From c360c5dc439f20817266d9214bebf09725aa62c9 Mon Sep 17 00:00:00 2001 From: Sara Vieira Date: Fri, 15 Jul 2022 23:26:47 +0200 Subject: [PATCH 15/20] revert image conflict; remiz does it by himself --- apps/designer/app/routes/designer/$id.tsx | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/apps/designer/app/routes/designer/$id.tsx b/apps/designer/app/routes/designer/$id.tsx index 2471ad868d..cf51b24585 100644 --- a/apps/designer/app/routes/designer/$id.tsx +++ b/apps/designer/app/routes/designer/$id.tsx @@ -51,18 +51,17 @@ export const action: ActionFunction = async ({ request, params }) => { unstable_createFileUploadHandler({ maxPartSize: 10_000_000, directory, - file: ({ filename }) => `${randomId}_${filename}`, + file: ({ filename }) => filename, }) ); const imageInfo = ImageUpload.parse(formData.get("image")); const info = imageInfo as ImageUpload; - const fileName = `${randomId}_${info.name}`; const data = { type: info.type, - name: fileName, - path: `/uploads/${fileName}`, + name: info.name, + path: `/uploads/${info.name}`, }; db.assets.create(params.id, data); From e6d205648bc9fa4386256291b5bc1f7c54708012 Mon Sep 17 00:00:00 2001 From: Sara Vieira Date: Fri, 15 Jul 2022 23:54:01 +0200 Subject: [PATCH 16/20] support env variable --- apps/designer/app/routes/designer/$id.tsx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/apps/designer/app/routes/designer/$id.tsx b/apps/designer/app/routes/designer/$id.tsx index cf51b24585..40a3a43f34 100644 --- a/apps/designer/app/routes/designer/$id.tsx +++ b/apps/designer/app/routes/designer/$id.tsx @@ -1,4 +1,5 @@ import { useLoaderData } from "@remix-run/react"; +import path from "path"; import { ActionFunction, LoaderFunction, @@ -11,7 +12,6 @@ import * as db from "~/shared/db"; import config from "~/config"; import env from "~/env.server"; import { z } from "zod"; -import ObjectID from "bson-objectid"; export { links }; export const loader: LoaderFunction = async ({ params }) => { @@ -24,8 +24,6 @@ export const loader: LoaderFunction = async ({ params }) => { return { config, assets, project, env }; }; -const directory = "./public/uploads"; - type Data = { config: typeof config; project: Project; @@ -43,9 +41,12 @@ const ImageUpload = z.object({ type ImageUpload = z.infer; export const action: ActionFunction = async ({ request, params }) => { + const uploads = path.join(__dirname, "../public"); + const folderInPublic = process.env.FILE_UPLOAD_PATH || "uploads"; + const directory = path.join(uploads, folderInPublic); + if (params.id === undefined) throw new Error("Project id undefined"); try { - const randomId = ObjectID.toString(); const formData = await unstable_parseMultipartFormData( request, unstable_createFileUploadHandler({ @@ -56,12 +57,11 @@ export const action: ActionFunction = async ({ request, params }) => { ); const imageInfo = ImageUpload.parse(formData.get("image")); - const info = imageInfo as ImageUpload; const data = { type: info.type, name: info.name, - path: `/uploads/${info.name}`, + path: `/${path.join(folderInPublic, info.name)}`, }; db.assets.create(params.id, data); From 7a99a8e590ca680937520ec9b9713a7af706203c Mon Sep 17 00:00:00 2001 From: Sara Vieira Date: Fri, 15 Jul 2022 23:55:03 +0200 Subject: [PATCH 17/20] add docs --- apps/designer/.env.example | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/apps/designer/.env.example b/apps/designer/.env.example index 346a8f74da..0c377438b8 100644 --- a/apps/designer/.env.example +++ b/apps/designer/.env.example @@ -2,4 +2,8 @@ DATABASE_URL=postgresql://user:pass@localhost/webstudio AUTH_SECRET="# Linux: `openssl rand -hex 32` or go to https://generate-secret.now.sh/32" GH_CLIENT_SECRET= GH_CLIENT_ID= -DEV_LOGIN=true \ No newline at end of file +DEV_LOGIN=true + + +# relative to the public directory +FILE_UPLOAD_PATH="uploads" \ No newline at end of file From db4f7ac642784fe78955f282b7df43ff7696ca86 Mon Sep 17 00:00:00 2001 From: Sara Vieira Date: Fri, 15 Jul 2022 23:57:19 +0200 Subject: [PATCH 18/20] remove useless cast --- apps/designer/app/routes/designer/$id.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/designer/app/routes/designer/$id.tsx b/apps/designer/app/routes/designer/$id.tsx index 40a3a43f34..5ca0dbaddf 100644 --- a/apps/designer/app/routes/designer/$id.tsx +++ b/apps/designer/app/routes/designer/$id.tsx @@ -57,11 +57,11 @@ export const action: ActionFunction = async ({ request, params }) => { ); const imageInfo = ImageUpload.parse(formData.get("image")); - const info = imageInfo as ImageUpload; + const data = { - type: info.type, - name: info.name, - path: `/${path.join(folderInPublic, info.name)}`, + type: imageInfo.type, + name: imageInfo.name, + path: `/${path.join(folderInPublic, imageInfo.name)}`, }; db.assets.create(params.id, data); From 00348991935a00746b68deb243274463dd347502 Mon Sep 17 00:00:00 2001 From: Sara Vieira Date: Sat, 16 Jul 2022 18:05:57 +0200 Subject: [PATCH 19/20] allow multiple uploads --- apps/designer/app/config.ts | 2 ++ .../sidebar-left/panels/image-upload.tsx | 1 + .../designer/features/sidebar-left/types.ts | 11 +++++++ apps/designer/app/routes/designer/$id.tsx | 32 +++++++++---------- apps/designer/app/shared/db/assets.server.ts | 7 ++-- 5 files changed, 31 insertions(+), 22 deletions(-) diff --git a/apps/designer/app/config.ts b/apps/designer/app/config.ts index 7bc8840d93..4ceb4e50d7 100644 --- a/apps/designer/app/config.ts +++ b/apps/designer/app/config.ts @@ -8,6 +8,8 @@ const config = { googleCallbackPath: "/google/callback", githubCallbackPath: "/github/callback", authPath: "/auth", + + defaultUploadPath: "uploads", }; export default config; export type Config = typeof config; diff --git a/apps/designer/app/designer/features/sidebar-left/panels/image-upload.tsx b/apps/designer/app/designer/features/sidebar-left/panels/image-upload.tsx index db40227b79..e7497524ba 100644 --- a/apps/designer/app/designer/features/sidebar-left/panels/image-upload.tsx +++ b/apps/designer/app/designer/features/sidebar-left/panels/image-upload.tsx @@ -28,6 +28,7 @@ export const TabContent = ({ assets }: { assets: Array }) => { accept="image/*" type="file" name="image" + multiple ref={inputRef} onChange={onChange} style={{ display: "none" }} diff --git a/apps/designer/app/designer/features/sidebar-left/types.ts b/apps/designer/app/designer/features/sidebar-left/types.ts index dc658bb5f1..4603d31968 100644 --- a/apps/designer/app/designer/features/sidebar-left/types.ts +++ b/apps/designer/app/designer/features/sidebar-left/types.ts @@ -1,3 +1,14 @@ import { panels } from "./panels"; +import { z } from "zod"; export type TabName = keyof typeof panels | "none"; + +const SingleImageInUpload = z.object({ + name: z.string(), + type: z.string(), +}); + +export const ImagesUpload = z.array(SingleImageInUpload); + +export type ImagesUpload = z.infer; +export type SingleImageInUpload = z.infer; diff --git a/apps/designer/app/routes/designer/$id.tsx b/apps/designer/app/routes/designer/$id.tsx index 5ca0dbaddf..4b9c888608 100644 --- a/apps/designer/app/routes/designer/$id.tsx +++ b/apps/designer/app/routes/designer/$id.tsx @@ -11,7 +11,8 @@ import { Designer, links } from "~/designer"; import * as db from "~/shared/db"; import config from "~/config"; import env from "~/env.server"; -import { z } from "zod"; +import { ImagesUpload } from "~/designer/features/sidebar-left/types"; + export { links }; export const loader: LoaderFunction = async ({ params }) => { @@ -34,18 +35,14 @@ type Error = { errors: "string"; }; -const ImageUpload = z.object({ - name: z.string(), - type: z.string(), -}); -type ImageUpload = z.infer; - export const action: ActionFunction = async ({ request, params }) => { + if (params.id === undefined) throw new Error("Project id undefined"); + const uploads = path.join(__dirname, "../public"); - const folderInPublic = process.env.FILE_UPLOAD_PATH || "uploads"; + const folderInPublic = + process.env.FILE_UPLOAD_PATH || config.defaultUploadPath; const directory = path.join(uploads, folderInPublic); - if (params.id === undefined) throw new Error("Project id undefined"); try { const formData = await unstable_parseMultipartFormData( request, @@ -56,14 +53,15 @@ export const action: ActionFunction = async ({ request, params }) => { }) ); - const imageInfo = ImageUpload.parse(formData.get("image")); - - const data = { - type: imageInfo.type, - name: imageInfo.name, - path: `/${path.join(folderInPublic, imageInfo.name)}`, - }; - db.assets.create(params.id, data); + const imagesInfo = ImagesUpload.parse(formData.getAll("image")); + imagesInfo.forEach((image) => { + const data = { + type: image.type, + name: image.name, + path: `${path.join("/", folderInPublic, image.name)}`, + }; + db.assets.create(params.id, data); + }); return { ok: true, diff --git a/apps/designer/app/shared/db/assets.server.ts b/apps/designer/app/shared/db/assets.server.ts index 92ddc1739d..7d0df52ea4 100644 --- a/apps/designer/app/shared/db/assets.server.ts +++ b/apps/designer/app/shared/db/assets.server.ts @@ -1,4 +1,4 @@ -import { type Project } from "@webstudio-is/react-sdk"; +import { Asset, type Project } from "@webstudio-is/react-sdk"; import { prisma } from "./prisma.server"; export const loadByProject = async (projectId?: Project["id"]) => { @@ -13,10 +13,7 @@ export const loadByProject = async (projectId?: Project["id"]) => { return assets; }; -export const create = async ( - projectId: Project["id"], - values: { type: string; name: string; path: string } -) => { +export const create = async (projectId: Project["id"], values: Asset) => { const newAsset = await prisma.asset.create({ data: { ...values, From f45a6767dba0cac2b3dd951ea7f4a10cbc00e878 Mon Sep 17 00:00:00 2001 From: Sara Vieira Date: Sat, 16 Jul 2022 18:10:42 +0200 Subject: [PATCH 20/20] fix ts & PR comment --- .../designer/features/sidebar-left/panels/image-upload.tsx | 6 +----- apps/designer/app/routes/designer/$id.tsx | 3 ++- apps/designer/app/shared/db/assets.server.ts | 7 +++++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/apps/designer/app/designer/features/sidebar-left/panels/image-upload.tsx b/apps/designer/app/designer/features/sidebar-left/panels/image-upload.tsx index e7497524ba..6214ae4933 100644 --- a/apps/designer/app/designer/features/sidebar-left/panels/image-upload.tsx +++ b/apps/designer/app/designer/features/sidebar-left/panels/image-upload.tsx @@ -17,11 +17,7 @@ export const TabContent = ({ assets }: { assets: Array }) => { return ( - + Assets
{ name: image.name, path: `${path.join("/", folderInPublic, image.name)}`, }; - db.assets.create(params.id, data); + const projectId = params.id as string; + db.assets.create(projectId, data); }); return { diff --git a/apps/designer/app/shared/db/assets.server.ts b/apps/designer/app/shared/db/assets.server.ts index 7d0df52ea4..92ddc1739d 100644 --- a/apps/designer/app/shared/db/assets.server.ts +++ b/apps/designer/app/shared/db/assets.server.ts @@ -1,4 +1,4 @@ -import { Asset, type Project } from "@webstudio-is/react-sdk"; +import { type Project } from "@webstudio-is/react-sdk"; import { prisma } from "./prisma.server"; export const loadByProject = async (projectId?: Project["id"]) => { @@ -13,7 +13,10 @@ export const loadByProject = async (projectId?: Project["id"]) => { return assets; }; -export const create = async (projectId: Project["id"], values: Asset) => { +export const create = async ( + projectId: Project["id"], + values: { type: string; name: string; path: string } +) => { const newAsset = await prisma.asset.create({ data: { ...values,