From 91d4394822e7bc67827f9c7004d3313f7c202b9a Mon Sep 17 00:00:00 2001 From: victoriaplummer Date: Mon, 24 Jun 2024 16:55:07 -0400 Subject: [PATCH 1/6] QOL Updates --- .env.example | 10 +- README.md | 2 +- backend/auth/mydatabase.db | Bin 12288 -> 0 bytes backend/controllers/authController.js | 101 +++++++ backend/routes/authRoutes.js | 91 +------ frontend/package-lock.json | 249 +++++++++++++++++- frontend/package.json | 3 +- .../components/Examples/RegisterScripts.jsx | 160 +++++++++-- .../components/Misc/HostedScriptUploader.jsx | 2 +- .../components/Misc/InlineScriptUploader.jsx | 2 +- frontend/src/utils/axiosInstance.js | 2 +- frontend/vite.config.js | 20 +- package.json | 2 +- 13 files changed, 526 insertions(+), 118 deletions(-) create mode 100644 backend/controllers/authController.js diff --git a/.env.example b/.env.example index c21a21e..4c1097a 100644 --- a/.env.example +++ b/.env.example @@ -1,7 +1,7 @@ -CLIENT_ID= -CLIENT_SECRET= -SITE_ID= -PORT= +WEBFLOW_CLIENT_ID=XXX +WEBFLOW_CLIENT_SECRET=XXX +PORT=8080 +VITE_PORT=3000 +NGROK_AUTH_TOKEN=XXX SITE_TOKEN= -NGROK_AUTH_TOKEN= diff --git a/README.md b/README.md index 6896387..c112847 100644 --- a/README.md +++ b/README.md @@ -78,7 +78,7 @@ The frontend is a React application that outlines the steps needed to register a ```sh npm install - npm install-project // This will install the dependencies in the subdirectories + npm run install-project // This will install the dependencies in the subdirectories npm run dev ``` diff --git a/backend/auth/mydatabase.db b/backend/auth/mydatabase.db index 455ad49b41d2d30d1539e8ad23c59fdf8c8e8c98..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 100755 GIT binary patch literal 0 HcmV?d00001 literal 12288 zcmeI%L2uJA6bEp-ndq2Q%4M1=F7L?1G&M`ICYlgr%ybbY8)>GoQ^1r19+b_Pg+TWS+=qqby?K{;WAOHafKmY;|fB*y_009U<00O@WtbC*S z#OrK+IVy|kWFm{{Y$1y>nHS0OW1f~WDd*R6rklm9Ncgd!I12iG5z(KAZjlQAb*u&i z;ZQ^|orP3iuS>_GAA~w1x;Q_nCakM86mgx>a|*@fzCKNtry>%xRCg8R>q32Sq$d&K z$%Hp|Ivu{+Ez|5q)|1t3933(sN- zF9rj3e)n19!On{gXVY0OKQBJrsQ3CLU6ylwTth7qvtGJ-x8JO=TWaU+Tc$Py1Rwwb z2tWV=5P$##AOHafKmY>&l)x$9*j}}&j|7hFIo^SNXiCenGA}(C+3v`7a { + try { + // Get a token from .db + const token = await getToken("user"); + + // If no token found, redirect user to the Auth Screen + if (!token) { + console.log("No token found. Redirecting to auth screen..."); + return res.redirect("/auth"); + } else { + // If token found, redirect user to the frontend + console.log("Token found. Redirecting to frontend"); + return res.redirect("http://localhost:3000"); + } + } catch (error) { + console.error("Error handling token:", error); + res.status(500).send("Internal Server Error"); + } +}; + +// Start authorization flow +export const startAuthFlow = async (req, res) => { + try { + const siteToken = process.env.SITE_TOKEN; + + // If a site token is included in the .env file , store the token and bypass the auth screen + if (siteToken) { + await storeToken("user", siteToken); + console.log("Site token found and stored."); + return res.redirect("http://localhost:3000"); + } else { + // If the site token is not included in the .env file, use the Webflow Client ID to create an authorization URL + const publicUrl = await getNgrokUrl(); + const authorizeUrl = WebflowClient.authorizeURL({ + scope: scopes, + clientId: process.env.WEBFLOW_CLIENT_ID, + redirectUri: `${publicUrl}/auth/callback`, + }); + + // Redirect the user to the Auth URL + return res.redirect(authorizeUrl); + } + } catch (error) { + console.error("Error starting auth flow:", error); + res.status(500).send("Failed to start auth flow"); + } +}; + +// Handle Callback from Authorization Screen +export const handleAuthCallback = async (req, res) => { + // Get the Authorization Code from the query parameters + const { code } = req.query; + if (!code) { + return res.status(400).send("Authorization code is required"); + } + + try { + // Get the Access Token from Webflow using the Authorization Code + const publicUrl = await getNgrokUrl(); + const accessToken = await WebflowClient.getAccessToken({ + clientId: process.env.WEBFLOW_CLIENT_ID, + clientSecret: process.env.WEBFLOW_CLIENT_SECRET, + code: code, + redirectUri: `${publicUrl}/auth/callback`, + }); + + // Store the Access Token in the DB + await storeToken("user", accessToken); // Use access_token + console.log("Access token obtained and stored. Redirecting to frontend..."); + return res.redirect("http://localhost:3000"); + } catch (error) { + console.error("Error fetching access token:", error); + return res.status(500).send("Failed to fetch access token"); + } +}; diff --git a/backend/routes/authRoutes.js b/backend/routes/authRoutes.js index f6c6dff..17ab730 100755 --- a/backend/routes/authRoutes.js +++ b/backend/routes/authRoutes.js @@ -1,94 +1,19 @@ import express from "express"; -import { WebflowClient } from "webflow-api"; -import dotenv from "dotenv"; -import path from "path"; -import { fileURLToPath } from "url"; -import { getNgrokUrl } from "../utils/ngrokManager.js"; -import { storeToken, getToken } from "../auth/tokens.js"; - -// Convert URL to local file path -const __filename = fileURLToPath(import.meta.url); -const __dirname = path.dirname(__filename); - -// Get Environment Variables from .env file in route -dotenv.config({ path: path.resolve(__dirname, "../../.env") }); +import { + handleRoot, + startAuthFlow, + handleAuthCallback, +} from "../controllers/authController.js"; const router = express.Router(); -// Include scopes for your app. Be sure your App has been registered with the same scopes. -const scopes = [ - "sites:read", - "sites:write", - "pages:write", - "pages:read", - "pages:write", - "custom_code:read", - "custom_code:write", -]; - // Redirect root to Auth Screen -router.get("/", async (req, res) => { - try { - const token = await getToken("user"); - if (!token) { - console.log("No token found. Redirecting to auth screen..."); - return res.redirect("/auth"); - } else { - console.log("Token found. Redirecting to frontend"); - return res.redirect("http://localhost:3000"); - } - } catch (error) { - console.error("Error handling token:", error); - res.status(500).send("Internal Server Error"); - } -}); +router.get("/", handleRoot); // Route to start Auth Flow. Redirects to Webflow Auth screen -router.get("/auth", async (req, res) => { - try { - // Check if a user is using a Site Token. If so, store the site token in the database and skip auth screen - const siteToken = process.env.SITE_TOKEN; - if (siteToken) { - await storeToken("user", siteToken); - console.log("Site token found and stored."); - } else { - const publicUrl = await getNgrokUrl(); - const authorizeUrl = WebflowClient.authorizeURL({ - scope: scopes, - clientId: process.env.WEBFLOW_CLIENT_ID, - redirectUri: `${publicUrl}/auth/callback`, - }); - res.redirect(authorizeUrl); - } - } catch (error) { - console.error("Error starting auth flow:", error); - res.status(500).send("Failed to start auth flow"); - } -}); +router.get("/auth", startAuthFlow); // Callback URI to get code and access token -router.get("/auth/callback", async (req, res) => { - const { code } = req.query; - if (!code) { - return res.status(400).send("Authorization code is required"); - } - - try { - const publicUrl = await getNgrokUrl(); - const accessToken = await WebflowClient.getAccessToken({ - clientId: process.env.WEBFLOW_CLIENT_ID, - clientSecret: process.env.WEBFLOW_CLIENT_SECRET, - code: code, - redirectUri: `${publicUrl}/auth/callback`, - }); - - await storeToken("user", accessToken); // Use access_token - console.log("Access token obtained and stored. Redirecting to frontend..."); - res.redirect("http://localhost:3000"); - } catch (error) { - console.error("Error fetching access token:", error); - res.status(500).send("Failed to fetch access token"); - } -}); +router.get("/auth/callback", handleAuthCallback); export default router; diff --git a/frontend/package-lock.json b/frontend/package-lock.json index feeeb46..33e4b37 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -13,7 +13,8 @@ "@mui/material": "^5.15.20", "axios": "^1.7.2", "react": "^18.2.0", - "react-dom": "^18.2.0" + "react-dom": "^18.2.0", + "react-syntax-highlighter": "^15.5.0" }, "devDependencies": { "@types/react": "^18.2.66", @@ -1595,6 +1596,14 @@ "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", "dev": true }, + "node_modules/@types/hast": { + "version": "2.3.10", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-2.3.10.tgz", + "integrity": "sha512-McWspRw8xx8J9HurkVBfYj0xKoE25tOFlHGdx4MJ5xORQrMGZNqJhVQWaIbm6Oyla5kYOXtDiopzKRJzEOkwJw==", + "dependencies": { + "@types/unist": "^2" + } + }, "node_modules/@types/parse-json": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz", @@ -1631,6 +1640,11 @@ "@types/react": "*" } }, + "node_modules/@types/unist": { + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.10.tgz", + "integrity": "sha512-IfYcSBWE3hLpBg8+X2SEa8LVkJdJEkT2Ese2aaLs3ptGdVtABxndrMaxuFlQ1qdFf9Q5rDvDpxI3WwgvKFAsQA==" + }, "node_modules/@ungap/structured-clone": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", @@ -2029,6 +2043,33 @@ "node": ">=4" } }, + "node_modules/character-entities": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-1.2.4.tgz", + "integrity": "sha512-iBMyeEHxfVnIakwOuDXpVkc54HijNgCyQB2w0VfGQThle6NXn50zU6V/u+LDhxHcDUPojn6Kpga3PTAD8W1bQw==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-legacy": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-1.1.4.tgz", + "integrity": "sha512-3Xnr+7ZFS1uxeiUDvV02wQ+QDbc55o97tIV5zHScSPJpcLm/r0DFPcoY3tYRp+VZukxuMeKgXYmsXQHO05zQeA==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-reference-invalid": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-1.1.4.tgz", + "integrity": "sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/clsx": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", @@ -2061,6 +2102,15 @@ "node": ">= 0.8" } }, + "node_modules/comma-separated-tokens": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-1.0.8.tgz", + "integrity": "sha512-GHuDRO12Sypu2cV70d1dkA2EUmXHgntrzbpvOB+Qy+49ypNfGgFQIC2fhhXbnyrJRynDCAARsT7Ou0M6hirpfw==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -2801,6 +2851,18 @@ "reusify": "^1.0.4" } }, + "node_modules/fault": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/fault/-/fault-1.0.4.tgz", + "integrity": "sha512-CJ0HCB5tL5fYTEA7ToAq5+kTwd++Borf1/bifxd9iT70QcXr4MRrO3Llf8Ifs70q+SJcGHFtnIE/Nw6giCtECA==", + "dependencies": { + "format": "^0.2.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/file-entry-cache": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", @@ -2895,6 +2957,14 @@ "node": ">= 6" } }, + "node_modules/format": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/format/-/format-0.2.2.tgz", + "integrity": "sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==", + "engines": { + "node": ">=0.4.x" + } + }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -3149,6 +3219,39 @@ "node": ">= 0.4" } }, + "node_modules/hast-util-parse-selector": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-2.2.5.tgz", + "integrity": "sha512-7j6mrk/qqkSehsM92wQjdIgWM2/BW61u/53G6xmC8i1OmEdKLHbk419QKQUjz6LglWsfqoiHmyMRkP1BGjecNQ==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hastscript": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-6.0.0.tgz", + "integrity": "sha512-nDM6bvd7lIqDUiYEiu5Sl/+6ReP0BMk/2f4U/Rooccxkj0P5nm+acM5PrGJ/t5I8qPGiqZSE6hVAwZEdZIvP4w==", + "dependencies": { + "@types/hast": "^2.0.0", + "comma-separated-tokens": "^1.0.0", + "hast-util-parse-selector": "^2.0.0", + "property-information": "^5.0.0", + "space-separated-tokens": "^1.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/highlight.js": { + "version": "10.7.3", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz", + "integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==", + "engines": { + "node": "*" + } + }, "node_modules/hoist-non-react-statics": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", @@ -3221,6 +3324,28 @@ "node": ">= 0.4" } }, + "node_modules/is-alphabetical": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-1.0.4.tgz", + "integrity": "sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-alphanumerical": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-1.0.4.tgz", + "integrity": "sha512-UzoZUr+XfVz3t3v4KyGEniVL9BDRoQtY7tOyrRybkVNjDFWyo1yhXNGrrBTQxp3ib9BLAWs7k2YKBQsFRkZG9A==", + "dependencies": { + "is-alphabetical": "^1.0.0", + "is-decimal": "^1.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/is-array-buffer": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.4.tgz", @@ -3338,6 +3463,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-decimal": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-1.0.4.tgz", + "integrity": "sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -3386,6 +3520,15 @@ "node": ">=0.10.0" } }, + "node_modules/is-hexadecimal": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-1.0.4.tgz", + "integrity": "sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/is-map": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", @@ -3724,6 +3867,19 @@ "loose-envify": "cli.js" } }, + "node_modules/lowlight": { + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/lowlight/-/lowlight-1.20.0.tgz", + "integrity": "sha512-8Ktj+prEb1RoCPkEOrPMYUN/nCggB7qAWe3a7OpMjWQkh3l2RD5wKRQ+o8Q8YuI9RG/xs95waaI/E6ym/7NsTw==", + "dependencies": { + "fault": "^1.0.0", + "highlight.js": "~10.7.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/lru-cache": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", @@ -3976,6 +4132,23 @@ "node": ">=6" } }, + "node_modules/parse-entities": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-2.0.0.tgz", + "integrity": "sha512-kkywGpCcRYhqQIchaWqZ875wzpS/bMKhz5HnN3p7wveJTkTtyAB/AlnS0f8DFSqYW1T82t6yEAkEcB+A1I3MbQ==", + "dependencies": { + "character-entities": "^1.0.0", + "character-entities-legacy": "^1.0.0", + "character-reference-invalid": "^1.0.0", + "is-alphanumerical": "^1.0.0", + "is-decimal": "^1.0.0", + "is-hexadecimal": "^1.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/parse-json": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", @@ -4084,6 +4257,14 @@ "node": ">= 0.8.0" } }, + "node_modules/prismjs": { + "version": "1.29.0", + "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.29.0.tgz", + "integrity": "sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q==", + "engines": { + "node": ">=6" + } + }, "node_modules/prop-types": { "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", @@ -4094,6 +4275,18 @@ "react-is": "^16.13.1" } }, + "node_modules/property-information": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/property-information/-/property-information-5.6.0.tgz", + "integrity": "sha512-YUHSPk+A30YPv+0Qf8i9Mbfe/C0hdPXk1s1jPVToV8pk8BQtpw10ct89Eo7OWkutrwqvT0eicAxlOg3dOAu8JA==", + "dependencies": { + "xtend": "^4.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/proxy-from-env": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", @@ -4165,6 +4358,21 @@ "node": ">=0.10.0" } }, + "node_modules/react-syntax-highlighter": { + "version": "15.5.0", + "resolved": "https://registry.npmjs.org/react-syntax-highlighter/-/react-syntax-highlighter-15.5.0.tgz", + "integrity": "sha512-+zq2myprEnQmH5yw6Gqc8lD55QHnpKaU8TOcFeC/Lg/MQSs8UknEA0JC4nTZGFAXC2J2Hyj/ijJ7NlabyPi2gg==", + "dependencies": { + "@babel/runtime": "^7.3.1", + "highlight.js": "^10.4.1", + "lowlight": "^1.17.0", + "prismjs": "^1.27.0", + "refractor": "^3.6.0" + }, + "peerDependencies": { + "react": ">= 0.14.0" + } + }, "node_modules/react-transition-group": { "version": "4.4.5", "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", @@ -4201,6 +4409,28 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/refractor": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/refractor/-/refractor-3.6.0.tgz", + "integrity": "sha512-MY9W41IOWxxk31o+YvFCNyNzdkc9M20NoZK5vq6jkv4I/uh2zkWcfudj0Q1fovjUQJrNewS9NMzeTtqPf+n5EA==", + "dependencies": { + "hastscript": "^6.0.0", + "parse-entities": "^2.0.0", + "prismjs": "~1.27.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/refractor/node_modules/prismjs": { + "version": "1.27.0", + "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.27.0.tgz", + "integrity": "sha512-t13BGPUlFDR7wRB5kQDG4jjl7XeuH6jbJGt11JHPL96qwsEHNX2+68tFXqc1/k+/jALsbSWJKUOT/hcYAZ5LkA==", + "engines": { + "node": ">=6" + } + }, "node_modules/regenerator-runtime": { "version": "0.14.1", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", @@ -4473,6 +4703,15 @@ "node": ">=0.10.0" } }, + "node_modules/space-separated-tokens": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-1.1.5.tgz", + "integrity": "sha512-q/JSVd1Lptzhf5bkYm4ob4iWPjx0KiRe3sRFBNrVqbJkFaBm5vbbowy1mymoPNLRa52+oadOhJ+K49wsSeSjTA==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/string.prototype.matchall": { "version": "4.0.11", "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.11.tgz", @@ -4928,6 +5167,14 @@ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "dev": true }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "engines": { + "node": ">=0.4" + } + }, "node_modules/yallist": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", diff --git a/frontend/package.json b/frontend/package.json index 0350079..36e2362 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -15,7 +15,8 @@ "@mui/material": "^5.15.20", "axios": "^1.7.2", "react": "^18.2.0", - "react-dom": "^18.2.0" + "react-dom": "^18.2.0", + "react-syntax-highlighter": "^15.5.0" }, "devDependencies": { "@types/react": "^18.2.66", diff --git a/frontend/src/components/Examples/RegisterScripts.jsx b/frontend/src/components/Examples/RegisterScripts.jsx index 2a5c77b..a6cbb1a 100644 --- a/frontend/src/components/Examples/RegisterScripts.jsx +++ b/frontend/src/components/Examples/RegisterScripts.jsx @@ -1,20 +1,117 @@ -import React, { useState, useEffect } from "react"; -import { Typography, Button, Box, Snackbar, Alert } from "@mui/material"; +import React, { useState, useEffect, useRef } from "react"; +import { + Typography, + Button, + Box, + Snackbar, + Alert, + TextField, + Paper, + FormControlLabel, + Checkbox, +} from "@mui/material"; +import { styled } from "@mui/system"; import axiosInstance from "../../utils/axiosInstance"; +import { Prism as SyntaxHighlighter } from "react-syntax-highlighter"; +import { vs } from "react-syntax-highlighter/dist/esm/styles/prism"; + +const ScriptItem = ({ + script, + type, + onRegister, + isRegistered, + inlineScriptContent, + index, +}) => { + const displayNameRef = useRef(""); + const versionRef = useRef(""); + const [canCopy, setCanCopy] = useState(true); + + const Item = styled(Paper)(({ theme }) => ({ + backgroundColor: + index % 2 !== 0 ? theme.palette.action.hover : "transparent", + padding: theme.spacing(2), + marginBottom: theme.spacing(2), + })); + + const Pill = styled("span")(({ theme, type }) => ({ + display: "inline-block", + padding: "2px 8px", + borderRadius: "12px", + backgroundColor: + type === "hosted" ? theme.palette.success.main : theme.palette.info.main, + color: "#fff", + marginLeft: theme.spacing(1), + fontSize: "0.75rem", + })); + + const inputProps = { + style: { + width: "50%", + }, + }; + + const handleRegister = () => { + const displayName = displayNameRef.current.value; + const version = versionRef.current.value; + onRegister(script, type, displayName, version, canCopy); + }; -const ScriptItem = ({ script, type, onRegister, isRegistered }) => { return ( - - {script.displayName || script} + + + {script.displayName || script} + {type === "hosted" ? "Hosted" : "Inline"} + + + + setCanCopy(e.target.checked)} + disabled={isRegistered} + /> + } + label="Can Copy" + /> + {type === "inline" && inlineScriptContent && ( + + Inline Script Content: + + {inlineScriptContent} + + + )} - + ); }; @@ -23,8 +120,28 @@ const RegisterScripts = ({ selectedSite, example, onNext }) => { const [registeredScripts, setRegisteredScripts] = useState({}); const [openSnackbar, setOpenSnackbar] = useState(false); const [snackbarMessage, setSnackbarMessage] = useState(""); + const [inlineScriptContent, setInlineScriptContent] = useState(""); - const handleRegisterScript = async (script, type) => { + useEffect(() => { + if (example.inlineScript) { + fetch( + `http://localhost:${process.env.PORT}/example-scripts/${example.inlineScript}` + ) + .then((response) => response.text()) + .then((data) => setInlineScriptContent(data)) + .catch((error) => + console.error("Error fetching inline script content:", error) + ); + } + }, [example.inlineScript]); + + const handleRegisterScript = async ( + script, + type, + displayName, + version, + canCopy + ) => { try { let response; if (type === "hosted") { @@ -32,22 +149,19 @@ const RegisterScripts = ({ selectedSite, example, onNext }) => { `/custom-code/scripts/${selectedSite.id}/hosted`, { hostedLocation: script, - version: "1.1.0", - displayName: `${example.name}header`, + version: version || "1.0.0", + displayName: displayName || `${example.name} Hosted Script`, + canCopy, } ); } else if (type === "inline") { - const inlineResponse = await fetch( - `http://localhost:8080/example-scripts/${script}` - ); - const sourceCode = await inlineResponse.text(); response = await axiosInstance.post( `/custom-code/scripts/${selectedSite.id}/inline`, { - sourceCode, - canCopy: true, - version: "1.1.0", - displayName: `${example.name}footer`, + sourceCode: inlineScriptContent, + canCopy, + version: version || "1.0.0", + displayName: displayName || `${example.name} Inline Script`, } ); } @@ -73,13 +187,14 @@ const RegisterScripts = ({ selectedSite, example, onNext }) => { return (
Register Scripts for {example.name} - {example.hostedScripts.map((script) => ( + {example.hostedScripts.map((script, index) => ( ))} { type="inline" onRegister={handleRegisterScript} isRegistered={registeredScripts[example.inlineScript]} + inlineScriptContent={inlineScriptContent} + index={example.hostedScripts.length} // Ensure unique index /> {status}