diff --git a/apps/web/.gitignore b/apps/web/.gitignore index 46f3800f7b..e178e70240 100644 --- a/apps/web/.gitignore +++ b/apps/web/.gitignore @@ -64,3 +64,6 @@ tsconfig.tsbuildinfo # Autogenerated embed content public/embed + +# Copied app-store images +public/app-store \ No newline at end of file diff --git a/apps/web/next.config.js b/apps/web/next.config.js index 789e2f3e83..f829e319ba 100644 --- a/apps/web/next.config.js +++ b/apps/web/next.config.js @@ -1,4 +1,5 @@ require("dotenv").config({ path: "../../.env" }); +const CopyWebpackPlugin = require("copy-webpack-plugin"); const withTM = require("next-transpile-modules")([ "@calcom/app-store", @@ -77,7 +78,26 @@ plugins.push(withAxiom); /** @type {import("next").NextConfig} */ const nextConfig = { i18n, + experimental: { + images: { + unoptimized: true, + }, + }, webpack: (config) => { + config.plugins.push( + new CopyWebpackPlugin({ + patterns: [ + { + from: "../../packages/app-store/*/static/**", + to({ context, absoluteFilename }) { + const appName = /app-store\/(.*)\/static/.exec(absoluteFilename); + return Promise.resolve(`${context}/public/app-store/${appName[1]}/[name][ext]`); + }, + }, + ], + }) + ); + config.resolve.fallback = { ...config.resolve.fallback, // if you miss it, all the other options in fallback, specified // by next.js will be dropped. Doesn't make much sense, but how it is @@ -121,6 +141,11 @@ const nextConfig = { }, async redirects() { const redirects = [ + { + source: "/api/app-store/:path*", + destination: "/app-store/:path*", + permanent: true, + }, { source: "/settings", destination: "/settings/profile", diff --git a/apps/web/package.json b/apps/web/package.json index 7b704ae045..f804fd4add 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -151,6 +151,7 @@ "@types/uuid": "8.3.1", "autoprefixer": "^10.4.7", "babel-jest": "^28.1.0", + "copy-webpack-plugin": "^11.0.0", "env-cmd": "^10.1.0", "eslint": "^8.20.0", "jest": "^28.1.0", diff --git a/apps/web/pages/api/app-store/[...static].ts b/apps/web/pages/api/app-store/[...static].ts deleted file mode 100644 index 2f83ec9460..0000000000 --- a/apps/web/pages/api/app-store/[...static].ts +++ /dev/null @@ -1,47 +0,0 @@ -import fs from "fs"; -import mime from "mime-types"; -import type { NextApiRequest, NextApiResponse } from "next"; -import path from "path"; - -/** - * This endpoint should allow us to access to the private files in the static - * folder of each individual app in the App Store. - * @example - * ```text - * Requesting: `/api/app-store/zoomvideo/icon.svg` from a public URL should - * serve us the file located at: `/packages/app-store/zoomvideo/static/icon.svg` - * ``` - * This will allow us to keep all app-specific static assets in the same directory. - */ -export default async function handler(req: NextApiRequest, res: NextApiResponse) { - const queryParts = Array.isArray(req.query.static) ? req.query.static : [req.query.static]; - let appPath, fileName; - if (queryParts[0] === "ee") { - const appName = queryParts[1]; - if (!appName) { - return res.status(400).json({ error: true, message: "No app name provided" }); - } - appPath = path.join("ee", appName); - fileName = queryParts[2]; - } else { - [appPath, fileName] = queryParts; - } - - if (!fileName) { - return res.status(400).json({ error: true, message: "No file name provided" }); - } - if (!appPath) { - return res.status(400).json({ error: true, message: "No app name provided" }); - } - const fileNameParts = fileName.split("."); - const { [fileNameParts.length - 1]: fileExtension } = fileNameParts; - const STATIC_PATH = path.join(process.cwd(), "..", "..", "packages/app-store", appPath, "static", fileName); - try { - const imageBuffer = fs.readFileSync(STATIC_PATH); - const mimeType = mime.lookup(fileExtension); - if (mimeType) res.setHeader("Content-Type", mimeType); - res.send(imageBuffer); - } catch (e) { - res.status(400).json({ error: true, message: "Resource not found" }); - } -} diff --git a/yarn.lock b/yarn.lock index c3db8d9e8a..7bf243f2d8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7182,11 +7182,25 @@ ajv-errors@^1.0.0: resolved "https://registry.yarnpkg.com/ajv-errors/-/ajv-errors-1.0.1.tgz#f35986aceb91afadec4102fbd85014950cefa64d" integrity sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ== +ajv-formats@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ajv-formats/-/ajv-formats-2.1.1.tgz#6e669400659eb74973bbf2e33327180a0996b520" + integrity sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA== + dependencies: + ajv "^8.0.0" + ajv-keywords@^3.1.0, ajv-keywords@^3.4.1, ajv-keywords@^3.5.2: version "3.5.2" resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d" integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ== +ajv-keywords@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-5.1.0.tgz#69d4d385a4733cdbeab44964a1170a88f87f0e16" + integrity sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw== + dependencies: + fast-deep-equal "^3.1.3" + ajv@^6.1.0, ajv@^6.10.0, ajv@^6.10.2, ajv@^6.12.2, ajv@^6.12.3, ajv@^6.12.4, ajv@^6.12.5: version "6.12.6" resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" @@ -7197,6 +7211,16 @@ ajv@^6.1.0, ajv@^6.10.0, ajv@^6.10.2, ajv@^6.12.2, ajv@^6.12.3, ajv@^6.12.4, ajv json-schema-traverse "^0.4.1" uri-js "^4.2.2" +ajv@^8.0.0, ajv@^8.8.0: + version "8.11.0" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.11.0.tgz#977e91dd96ca669f54a11e23e378e33b884a565f" + integrity sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg== + dependencies: + fast-deep-equal "^3.1.1" + json-schema-traverse "^1.0.0" + require-from-string "^2.0.2" + uri-js "^4.2.2" + ansi-align@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/ansi-align/-/ansi-align-3.0.1.tgz#0cdf12e111ace773a86e9a1fad1225c43cb19a59" @@ -9423,6 +9447,18 @@ copy-to-clipboard@^3: dependencies: toggle-selection "^1.0.6" +copy-webpack-plugin@^11.0.0: + version "11.0.0" + resolved "https://registry.yarnpkg.com/copy-webpack-plugin/-/copy-webpack-plugin-11.0.0.tgz#96d4dbdb5f73d02dd72d0528d1958721ab72e04a" + integrity sha512-fX2MWpamkW0hZxMEg0+mYnA40LTosOSa5TqZ9GYIBzyJa9C3QUaMPSE2xAi/buNr8u89SfD9wHSQVBzrRa/SOQ== + dependencies: + fast-glob "^3.2.11" + glob-parent "^6.0.1" + globby "^13.1.1" + normalize-path "^3.0.0" + schema-utils "^4.0.0" + serialize-javascript "^6.0.0" + core-js-compat@^3.21.0, core-js-compat@^3.22.1, core-js-compat@^3.8.1: version "3.23.4" resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.23.4.tgz#56ad4a352884317a15f6b04548ff7139d23b917f" @@ -12634,6 +12670,17 @@ globby@^11.0.2, globby@^11.1.0: merge2 "^1.4.1" slash "^3.0.0" +globby@^13.1.1: + version "13.1.2" + resolved "https://registry.yarnpkg.com/globby/-/globby-13.1.2.tgz#29047105582427ab6eca4f905200667b056da515" + integrity sha512-LKSDZXToac40u8Q1PQtZihbNdTYSNMuWe+K5l+oa6KgDzSvVrHXlJy40hUP522RjAIoNLJYBJi7ow+rbFpIhHQ== + dependencies: + dir-glob "^3.0.1" + fast-glob "^3.2.11" + ignore "^5.2.0" + merge2 "^1.4.1" + slash "^4.0.0" + globby@^9.2.0: version "9.2.0" resolved "https://registry.yarnpkg.com/globby/-/globby-9.2.0.tgz#fd029a706c703d29bdd170f4b6db3a3f7a7cb63d" @@ -14959,6 +15006,11 @@ json-schema-traverse@^0.4.1: resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== +json-schema-traverse@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2" + integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== + json-schema@0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.4.0.tgz#f7de4cf6efab838ebaeb3236474cbba5a1930ab5" @@ -20057,6 +20109,11 @@ require-directory@^2.1.1: resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= +require-from-string@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" + integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== + require-main-filename@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" @@ -20409,6 +20466,16 @@ schema-utils@^3.0.0, schema-utils@^3.1.0, schema-utils@^3.1.1: ajv "^6.12.5" ajv-keywords "^3.5.2" +schema-utils@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-4.0.0.tgz#60331e9e3ae78ec5d16353c467c34b3a0a1d3df7" + integrity sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg== + dependencies: + "@types/json-schema" "^7.0.9" + ajv "^8.8.0" + ajv-formats "^2.1.1" + ajv-keywords "^5.0.0" + scmp@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/scmp/-/scmp-2.1.0.tgz#37b8e197c425bdeb570ab91cc356b311a11f9c9a" @@ -20736,6 +20803,11 @@ slash@^3.0.0: resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== +slash@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-4.0.0.tgz#2422372176c4c6c5addb5e2ada885af984b396a7" + integrity sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew== + slice-ansi@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-3.0.0.tgz#31ddc10930a1b7e0b67b08c96c2f49b77a789787"