From 68fccc913dea4a70c1cc12d0a0b5c4d3e6c872dd Mon Sep 17 00:00:00 2001 From: Boid Date: Sat, 2 Nov 2024 01:03:58 -0400 Subject: [PATCH] edge function for image request --- .vscode/settings.json | 22 +++++-- index.html | 37 ++++++------ netlify.toml | 6 +- netlify/deno.json | 3 + netlify/edge-functions/handleRequestPage.ts | 25 ++++---- netlify/edge-functions/lib/util.ts | 41 +++++++++++++ netlify/edge-functions/tsconfig.json | 11 ++++ netlify/functions/renderMetadata.ts | 64 --------------------- netlify/functions/tsconfig.json | 10 ---- package.json | 2 +- public/sitemap.xml | 38 +++++++----- src/components/dialogs/ImageGallery.vue | 4 +- src/lib/socialMeta.ts | 57 +++++++++++------- src/pages/BrowsePage.vue | 6 +- src/pages/ClaimPage.vue | 2 +- src/pages/CreatePage.vue | 4 +- src/pages/ImageRequestPage.vue | 8 +-- 17 files changed, 177 insertions(+), 163 deletions(-) create mode 100644 netlify/deno.json create mode 100644 netlify/edge-functions/lib/util.ts create mode 100644 netlify/edge-functions/tsconfig.json delete mode 100644 netlify/functions/renderMetadata.ts delete mode 100644 netlify/functions/tsconfig.json diff --git a/.vscode/settings.json b/.vscode/settings.json index 1f65da4..ac6eae6 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -3,8 +3,15 @@ "editor.guides.bracketPairs": true, "editor.formatOnSave": true, "editor.defaultFormatter": "dbaeumer.vscode-eslint", - "editor.codeActionsOnSave": ["source.fixAll.eslint"], - "eslint.validate": ["javascript", "javascriptreact", "typescript", "vue"], + "editor.codeActionsOnSave": [ + "source.fixAll.eslint" + ], + "eslint.validate": [ + "javascript", + "javascriptreact", + "typescript", + "vue" + ], "typescript.tsdk": "node_modules/typescript/lib", "[vue]": { "editor.defaultFormatter": "esbenp.prettier-vscode" @@ -14,5 +21,12 @@ }, "[html]": { "editor.defaultFormatter": "vscode.html-language-features" - } -} + }, + "deno.enable": true, + "deno.enablePaths": [ + "netlify/edge-functions" + ], + "deno.unstable": true, + "deno.importMap": ".netlify/edge-functions-import-map.json", + "deno.path": "/Users/boidcom/Library/Preferences/netlify/deno-cli/deno" +} \ No newline at end of file diff --git a/index.html b/index.html index c315141..8289f76 100644 --- a/index.html +++ b/index.html @@ -2,38 +2,33 @@ + + - - Fiddl.art - - + + + + - - - - - + + + + - - - - - + + + + - - - - - - + diff --git a/netlify.toml b/netlify.toml index 8be3d2e..54dab60 100644 --- a/netlify.toml +++ b/netlify.toml @@ -2,12 +2,14 @@ base = "." publish = "dist/spa" command = "yarn build" - # functions = "netlify/functions/dist" [[edge_functions]] function = "handleRequestPage" path = "/r/*" +[functions] + included_files = ["netlify/edge-functions/lib/**"] + # [[redirects]] # from = "/r/*" # to = "/.netlify/functions/renderMetadata" @@ -21,3 +23,5 @@ [images] remote_images = ["https://api.fiddl.art/images/.*"] + + diff --git a/netlify/deno.json b/netlify/deno.json new file mode 100644 index 0000000..3dbc1ca --- /dev/null +++ b/netlify/deno.json @@ -0,0 +1,3 @@ +{ + "type": "module" +} diff --git a/netlify/edge-functions/handleRequestPage.ts b/netlify/edge-functions/handleRequestPage.ts index cd58a4b..86c1133 100644 --- a/netlify/edge-functions/handleRequestPage.ts +++ b/netlify/edge-functions/handleRequestPage.ts @@ -1,20 +1,17 @@ import { Context } from "@netlify/edge-functions" - +import { setSocialMetadata } from "./lib/util.ts" export default async (request: Request, context: Context) => { const url = new URL(request.url) - // request. - // Since this function is only triggered by the path defined in netlify.toml, no need to check the URL path - const metaTags = ` - - - - ` - const response = await context.next() + const text = await response.text() + for (const [key] of url.searchParams) { + if (key !== "index") { + url.searchParams.delete(key) + } + } - // Modify the HTML response - let text = await response.text() - text = text.replace("", `${metaTags}`) - - return new Response(text, response) + const segments = url.pathname.split("/") + const id = segments[2] + const updatedHtml = setSocialMetadata(text, id || "image id", "My Description", "https://api.fiddl.art/images/07f9e0f4-cb76-4408-9ed4-7815781bd957-lg.webp", url.toString()) + return new Response(updatedHtml, response) } diff --git a/netlify/edge-functions/lib/util.ts b/netlify/edge-functions/lib/util.ts new file mode 100644 index 0000000..d5e9817 --- /dev/null +++ b/netlify/edge-functions/lib/util.ts @@ -0,0 +1,41 @@ +export function setSocialMetadata(html: string, title: string, description: string, imageUrl: string, canonicalUrl: string): string { + // Helper to create or update meta tags in HTML content + const updateMetaTag = (html: string, property: string, content: string, attribute: string = "content"): string => { + // Matching meta tags with flexible attribute formats + const metaTagRegex = new RegExp(`]*?property=["']?${property}["']?\\s+[^>]*${attribute}=["']?[^"']*["']?\\s*/?>`, "gi") + const newMetaTag = `` + + if (html.search(metaTagRegex) !== -1) { + // Replace existing meta tag + return html.replace(metaTagRegex, newMetaTag) + } else { + // Append new meta tag if not found + return html.replace("", ` ${newMetaTag}\n`) + } + } + + const updateLinkTag = (html: string, rel: string, href: string): string => { + // Matching link tags with flexible attribute formats + const linkTagRegex = new RegExp(`]*?rel=["']?${rel}["']?\\s+[^>]*href=["']?[^"']*["']?\\s*/?>`, "gi") + const newLinkTag = `` + + if (html.search(linkTagRegex) !== -1) { + // Replace existing link tag + return html.replace(linkTagRegex, newLinkTag) + } else { + // Append new link tag if not found + return html.replace("", ` ${newLinkTag}\n`) + } + } + + // Update meta tags + html = updateMetaTag(html, "og:title", title) + html = updateMetaTag(html, "og:description", description) + html = updateMetaTag(html, "og:image", imageUrl) + html = updateMetaTag(html, "twitter:title", title) + html = updateMetaTag(html, "twitter:description", description) + html = updateMetaTag(html, "twitter:image", imageUrl) + html = updateLinkTag(html, "canonical", canonicalUrl) + + return html +} diff --git a/netlify/edge-functions/tsconfig.json b/netlify/edge-functions/tsconfig.json new file mode 100644 index 0000000..8cf4611 --- /dev/null +++ b/netlify/edge-functions/tsconfig.json @@ -0,0 +1,11 @@ +{ + "compilerOptions": { + "target": "ESNext", + "module": "NodeNext", + "strict": true, + "esModuleInterop": true, + "allowImportingTsExtensions": true, + "noEmit": true, + "moduleResolution": "NodeNext" + } +} diff --git a/netlify/functions/renderMetadata.ts b/netlify/functions/renderMetadata.ts deleted file mode 100644 index 8027c94..0000000 --- a/netlify/functions/renderMetadata.ts +++ /dev/null @@ -1,64 +0,0 @@ -import { Handler, builder } from "@netlify/functions" -// import { shortIdToLong } from "./util" - -interface Metadata { - title: string - description: string - image: string -} - -const generateMetadata = async (dynamicId: string, index: string): Promise => { - return { - title: `Page for ${dynamicId} - Index ${index}`, - description: `This is a description for ${dynamicId} at index ${index}.`, - image: "https://eospowerup.io/eospowerupio.jpg", - } -} - -const handler: Handler = builder(async (event) => { - const { path, queryStringParameters, headers } = event - const dynamicId = path?.split("/").pop() || "" - const index = queryStringParameters?.index || "1" - - const pageData = await generateMetadata(dynamicId, index) - - return { - statusCode: 200, - headers: { "Content-Type": "text/html" }, - body: ` - - - - - Page for StxXwHQySwGBzFrsGFJKIQ - Index 1 - - - - - - - - - - - - - - - - - - - - - -

${pageData.title}

-

${pageData.description}

- ${pageData.title} - - - `, - } -}) - -export { handler } diff --git a/netlify/functions/tsconfig.json b/netlify/functions/tsconfig.json deleted file mode 100644 index 868d938..0000000 --- a/netlify/functions/tsconfig.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "compilerOptions": { - "outDir": "./dist", // Output directory specific to functions - "rootDir": "./", // Sets root to current directory - "module": "commonjs", // Required for Node.js - "target": "es6" - }, - "include": ["**/*.ts"], // Only include .ts files in this directory - "exclude": ["../node_modules"] // Exclude node_modules in root -} diff --git a/package.json b/package.json index 01a87ef..b310d14 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,7 @@ "scripts": { "test": "echo \"No test specified\" && exit 0", "dev": "quasar dev", - "build": "quasar build && yarn build:func", + "build": "quasar build", "build:func": "tsc -p netlify/functions/tsconfig.json", "dev:func": "tsc -p netlify/functions/tsconfig.json --watch", "lint": "eslint --fix ./src", diff --git a/public/sitemap.xml b/public/sitemap.xml index a9fa782..1e6c43c 100644 --- a/public/sitemap.xml +++ b/public/sitemap.xml @@ -2,58 +2,66 @@ https://alpha.fiddl.art/ - 2024-10-31T16:56:59.707Z + 2024-11-02T04:53:07.158Z https://alpha.fiddl.art/stats - 2024-10-31T16:56:59.707Z + 2024-11-02T04:53:07.158Z https://alpha.fiddl.art/search - 2024-10-31T16:56:59.707Z + 2024-11-02T04:53:07.158Z https://alpha.fiddl.art/create - 2024-10-31T16:56:59.707Z + 2024-11-02T04:53:07.158Z https://alpha.fiddl.art/vote - 2024-10-31T16:56:59.707Z + 2024-11-02T04:53:07.158Z https://alpha.fiddl.art/mint - 2024-10-31T16:56:59.707Z + 2024-11-02T04:53:07.158Z https://alpha.fiddl.art/browse - 2024-10-31T16:56:59.707Z + 2024-11-02T04:53:07.158Z - https://alpha.fiddl.art/account - 2024-10-31T16:56:59.707Z + https://alpha.fiddl.art/a + 2024-11-02T04:53:07.158Z + + + https://alpha.fiddl.art/settings + 2024-11-02T04:53:07.158Z https://alpha.fiddl.art/creations/ - 2024-10-31T16:56:59.707Z + 2024-11-02T04:53:07.158Z https://alpha.fiddl.art/addPoints - 2024-10-31T16:56:59.707Z + 2024-11-02T04:53:07.158Z https://alpha.fiddl.art/login - 2024-10-31T16:56:59.707Z + 2024-11-02T04:53:07.158Z https://alpha.fiddl.art/r/ - 2024-10-31T16:56:59.707Z + 2024-11-02T04:53:07.158Z https://alpha.fiddl.art/admin - 2024-10-31T16:56:59.707Z + 2024-11-02T04:53:07.158Z https://alpha.fiddl.art/claim/ - 2024-10-31T16:56:59.707Z + 2024-11-02T04:53:07.158Z + + + https://alpha.fiddl.art/tos + 2024-11-02T04:53:07.158Z \ No newline at end of file diff --git a/src/components/dialogs/ImageGallery.vue b/src/components/dialogs/ImageGallery.vue index 44bbbcf..16f6e40 100644 --- a/src/components/dialogs/ImageGallery.vue +++ b/src/components/dialogs/ImageGallery.vue @@ -174,8 +174,8 @@ export default defineComponent({ }) } else { this.userLikedImage = !this.userLikedImage - if (this.userLikedImage) this.$api.collections.likeImage.mutate(this.currentImageId) - else this.$api.collections.unlikeImage.mutate(this.currentImageId) + if (this.userLikedImage) void this.$api.collections.likeImage.mutate(this.currentImageId) + else void this.$api.collections.unlikeImage.mutate(this.currentImageId) } }, editImage() { diff --git a/src/lib/socialMeta.ts b/src/lib/socialMeta.ts index a3d3c86..b0b16f8 100644 --- a/src/lib/socialMeta.ts +++ b/src/lib/socialMeta.ts @@ -1,25 +1,40 @@ -/** - * Dynamically sets social media meta tags for an SPA. - * @param title - The title of the page - * @param description - The description of the page - * @param imageUrl - The URL of the image to use for social previews - */ -export function setSocialMetadata(title: string, description: string, imageUrl: string): void { - // Helper to create or update meta tags - const updateMetaTag = (property: string, content: string) => { - let element = document.querySelector(`meta[property='${property}']`) as HTMLMetaElement | null - if (!element) { - element = document.createElement("meta") - element.setAttribute("property", property) - document.head.appendChild(element) +export function setSocialMetadata(html: string, title: string, description: string, imageUrl: string, canonicalUrl: string): string { + // Helper to create or update meta tags in HTML content + const updateMetaTag = (html: string, property: string, content: string, attribute: string = "content"): string => { + const metaTagRegex = new RegExp(`", ` ${newMetaTag}\n`) + } + } + + const updateLinkTag = (html: string, rel: string, href: string): string => { + const linkTagRegex = new RegExp(`", ` ${newLinkTag}\n`) } - element.setAttribute("content", content) } - updateMetaTag("og:title", title) - updateMetaTag("og:description", description) - updateMetaTag("og:image", imageUrl) - updateMetaTag("twitter:title", title) - updateMetaTag("twitter:description", description) - updateMetaTag("twitter:image", imageUrl) + html = updateMetaTag(html, "og:title", title) + html = updateMetaTag(html, "og:description", description) + html = updateMetaTag(html, "og:image", imageUrl) + html = updateMetaTag(html, "twitter:title", title) + html = updateMetaTag(html, "twitter:description", description) + html = updateMetaTag(html, "twitter:image", imageUrl) + html = updateLinkTag(html, "canonical", canonicalUrl) + + return html } diff --git a/src/pages/BrowsePage.vue b/src/pages/BrowsePage.vue index 8e57af7..44bd1fa 100644 --- a/src/pages/BrowsePage.vue +++ b/src/pages/BrowsePage.vue @@ -15,9 +15,9 @@ q-page.full-height.full-width