From 57243bc4b4d28e80e42ec70e3656e5a94c8407e2 Mon Sep 17 00:00:00 2001 From: Taqib Date: Wed, 23 Jul 2025 15:11:10 +0100 Subject: [PATCH 1/7] feat: add an rss feed --- apps/web/package.json | 1 + apps/web/src/app/metadata.ts | 32 ++++++++++++++----------- apps/web/src/app/rss.xml/route.ts | 40 +++++++++++++++++++++++++++++++ bun.lock | 9 +++++-- 4 files changed, 66 insertions(+), 16 deletions(-) create mode 100644 apps/web/src/app/rss.xml/route.ts diff --git a/apps/web/package.json b/apps/web/package.json index c409136c..1d16576e 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -38,6 +38,7 @@ "dotenv": "^16.5.0", "drizzle-orm": "^0.44.2", "embla-carousel-react": "^8.5.1", + "feed": "^5.1.0", "framer-motion": "^11.13.1", "input-otp": "^1.4.1", "lucide-react": "^0.468.0", diff --git a/apps/web/src/app/metadata.ts b/apps/web/src/app/metadata.ts index 6f9e6023..de8cf6f0 100644 --- a/apps/web/src/app/metadata.ts +++ b/apps/web/src/app/metadata.ts @@ -1,25 +1,29 @@ import { Metadata } from "next"; -const title = "OpenCut"; -const description = - "A simple but powerful video editor that gets the job done. In your browser."; -const openGraphImageUrl = "https://opencut.app/open-graph/default.jpg"; -const twitterImageUrl = "/open-graph/default.jpg"; +export const SITE_INFO = { + title: "OpenCut", + description: + "A simple but powerful video editor that gets the job done. In your browser.", + url: "https://opencut.app", + openGraphImage: "https://opencut.app/open-graph/default.jpg", + twitterImage: "/open-graph/default.jpg", + favicon: "/favicon.ico", +}; export const baseMetaData: Metadata = { metadataBase: new URL("https://opencut.app"), - title: title, - description: description, + title: SITE_INFO.title, + description: SITE_INFO.description, openGraph: { - title: title, - description: description, + title: SITE_INFO.title, + description: SITE_INFO.description, url: "https://opencut.app", siteName: "OpenCut", locale: "en_US", type: "website", images: [ { - url: openGraphImageUrl, + url: SITE_INFO.openGraphImage, width: 1200, height: 630, alt: "OpenCut", @@ -28,10 +32,10 @@ export const baseMetaData: Metadata = { }, twitter: { card: "summary_large_image", - title: title, - description: description, + title: SITE_INFO.title, + description: SITE_INFO.description, creator: "@opencutapp", - images: [twitterImageUrl], + images: [SITE_INFO.twitterImage], }, pinterest: { richPin: false, @@ -82,7 +86,7 @@ export const baseMetaData: Metadata = { }, appleWebApp: { capable: true, - title: title, + title: SITE_INFO.title, }, manifest: "/manifest.json", other: { diff --git a/apps/web/src/app/rss.xml/route.ts b/apps/web/src/app/rss.xml/route.ts new file mode 100644 index 00000000..1567d4a7 --- /dev/null +++ b/apps/web/src/app/rss.xml/route.ts @@ -0,0 +1,40 @@ +import { Feed } from 'feed'; +import { getPosts } from '@/lib/blog-query'; +import { SITE_INFO } from '../metadata'; + +export async function GET() { + const { posts } = await getPosts(); + + const feed = new Feed({ + title: 'OpenCut Blog', + description: SITE_INFO.description, + id: `${SITE_INFO.url}`, + link: `${SITE_INFO.url}/blog/`, + language: 'en', + image: `${SITE_INFO.openGraphImage}`, + favicon: `${SITE_INFO.favicon}`, + copyright: `All rights reserved ${new Date().getFullYear()}, ${ + SITE_INFO.title + }`, + }); + + posts.forEach((article) => { + feed.addItem({ + title: article.title, + id: `${SITE_INFO.url}/blog/${article.slug}`, + link: `${SITE_INFO.url}/blog/${article.slug}`, + description: article.description, + author: article.authors.map((author) => ({ + name: author.name ?? 'OpenCut', + })), + date: new Date(article.publishedAt), + image: article.coverImage, + }); + }); + + return new Response(feed.rss2(), { + headers: { + 'Content-Type': 'text/xml', + }, + }); +} \ No newline at end of file diff --git a/bun.lock b/bun.lock index 52cfff88..e986b2aa 100644 --- a/bun.lock +++ b/bun.lock @@ -39,6 +39,7 @@ "dotenv": "^16.5.0", "drizzle-orm": "^0.44.2", "embla-carousel-react": "^8.5.1", + "feed": "^5.1.0", "framer-motion": "^11.13.1", "input-otp": "^1.4.1", "lucide-react": "^0.468.0", @@ -675,6 +676,8 @@ "fastq": ["fastq@1.19.1", "", { "dependencies": { "reusify": "^1.0.4" } }, "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ=="], + "feed": ["feed@5.1.0", "", { "dependencies": { "xml-js": "^1.6.11" } }, "sha512-qGNhgYygnefSkAHHrNHqC7p3R8J0/xQDS/cYUud8er/qD9EFGWyCdUDfULHTJQN1d3H3WprzVwMc9MfB4J50Wg=="], + "fill-range": ["fill-range@7.1.1", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="], "foreground-child": ["foreground-child@3.3.1", "", { "dependencies": { "cross-spawn": "^7.0.6", "signal-exit": "^4.0.1" } }, "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw=="], @@ -1019,6 +1022,8 @@ "run-parallel": ["run-parallel@1.2.0", "", { "dependencies": { "queue-microtask": "^1.2.2" } }, "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA=="], + "sax": ["sax@1.4.1", "", {}, "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg=="], + "scheduler": ["scheduler@0.23.2", "", { "dependencies": { "loose-envify": "^1.1.0" } }, "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ=="], "semver": ["semver@7.7.2", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA=="], @@ -1151,6 +1156,8 @@ "wrap-ansi-cjs": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="], + "xml-js": ["xml-js@1.6.11", "", { "dependencies": { "sax": "^1.2.4" }, "bin": { "xml-js": "./bin/cli.js" } }, "sha512-7rVi2KMfwfWFl+GpPg6m80IVMWXLRjO+PxTq7V2CDhoGak0wzYzFgUY2m4XJ47OGdXd8eLE8EmwfAmdjw7lC1g=="], + "xtend": ["xtend@4.0.2", "", {}, "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ=="], "yaml": ["yaml@2.8.0", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-4lLa/EcQCB0cJkyts+FpIRx5G/llPxfP6VQU5KByHEhLxY3IJCH0f0Hy1MHI8sClTvsIb8qwRJ6R/ZdlDJ/leQ=="], @@ -1163,8 +1170,6 @@ "@esbuild-kit/core-utils/esbuild": ["esbuild@0.18.20", "", { "optionalDependencies": { "@esbuild/android-arm": "0.18.20", "@esbuild/android-arm64": "0.18.20", "@esbuild/android-x64": "0.18.20", "@esbuild/darwin-arm64": "0.18.20", "@esbuild/darwin-x64": "0.18.20", "@esbuild/freebsd-arm64": "0.18.20", "@esbuild/freebsd-x64": "0.18.20", "@esbuild/linux-arm": "0.18.20", "@esbuild/linux-arm64": "0.18.20", "@esbuild/linux-ia32": "0.18.20", "@esbuild/linux-loong64": "0.18.20", "@esbuild/linux-mips64el": "0.18.20", "@esbuild/linux-ppc64": "0.18.20", "@esbuild/linux-riscv64": "0.18.20", "@esbuild/linux-s390x": "0.18.20", "@esbuild/linux-x64": "0.18.20", "@esbuild/netbsd-x64": "0.18.20", "@esbuild/openbsd-x64": "0.18.20", "@esbuild/sunos-x64": "0.18.20", "@esbuild/win32-arm64": "0.18.20", "@esbuild/win32-ia32": "0.18.20", "@esbuild/win32-x64": "0.18.20" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA=="], - "@types/bun/bun-types": ["bun-types@1.2.19", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, "sha512-uAOTaZSPuYsWIXRpj7o56Let0g/wjihKCkeRqUBhlLVM/Bt+Fj9xTo+LhC1OV1XDaGkz4hNC80et5xgy+9KTHQ=="], - "better-auth/zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="], "chokidar/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], From 184dcbabefa357b12c2cce3e6c4ab87a975a33c6 Mon Sep 17 00:00:00 2001 From: Taqib Ibrahim Date: Wed, 23 Jul 2025 15:45:07 +0100 Subject: [PATCH 2/7] Update apps/web/src/app/rss.xml/route.ts Co-authored-by: Dominik K. --- apps/web/src/app/rss.xml/route.ts | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/apps/web/src/app/rss.xml/route.ts b/apps/web/src/app/rss.xml/route.ts index 1567d4a7..8602a03f 100644 --- a/apps/web/src/app/rss.xml/route.ts +++ b/apps/web/src/app/rss.xml/route.ts @@ -18,17 +18,17 @@ export async function GET() { }`, }); - posts.forEach((article) => { + for (const post of posts) { feed.addItem({ - title: article.title, - id: `${SITE_INFO.url}/blog/${article.slug}`, - link: `${SITE_INFO.url}/blog/${article.slug}`, - description: article.description, - author: article.authors.map((author) => ({ - name: author.name ?? 'OpenCut', + title: post.title, + id: `${SITE_INFO.url}/blog/${post.slug}`, + link: `${SITE_INFO.url}/blog/${post.slug}`, + description: post.description, + author: post.authors.map((author) => ({ + name: post.name ?? 'OpenCut', })), - date: new Date(article.publishedAt), - image: article.coverImage, + date: new Date(post.publishedAt), + image: post.coverImage, }); }); From f39f46788f77e56efb8e8f53983519ece993de74 Mon Sep 17 00:00:00 2001 From: Taqib Date: Wed, 23 Jul 2025 15:59:47 +0100 Subject: [PATCH 3/7] fix: add error handling --- apps/web/src/app/rss.xml/route.ts | 70 +++++++++++++++++-------------- 1 file changed, 38 insertions(+), 32 deletions(-) diff --git a/apps/web/src/app/rss.xml/route.ts b/apps/web/src/app/rss.xml/route.ts index 8602a03f..b16bd856 100644 --- a/apps/web/src/app/rss.xml/route.ts +++ b/apps/web/src/app/rss.xml/route.ts @@ -3,38 +3,44 @@ import { getPosts } from '@/lib/blog-query'; import { SITE_INFO } from '../metadata'; export async function GET() { - const { posts } = await getPosts(); - - const feed = new Feed({ - title: 'OpenCut Blog', - description: SITE_INFO.description, - id: `${SITE_INFO.url}`, - link: `${SITE_INFO.url}/blog/`, - language: 'en', - image: `${SITE_INFO.openGraphImage}`, - favicon: `${SITE_INFO.favicon}`, - copyright: `All rights reserved ${new Date().getFullYear()}, ${ - SITE_INFO.title - }`, - }); - - for (const post of posts) { - feed.addItem({ - title: post.title, - id: `${SITE_INFO.url}/blog/${post.slug}`, - link: `${SITE_INFO.url}/blog/${post.slug}`, - description: post.description, - author: post.authors.map((author) => ({ - name: post.name ?? 'OpenCut', - })), - date: new Date(post.publishedAt), - image: post.coverImage, + try { + const { posts } = await getPosts(); + + const feed = new Feed({ + title: 'OpenCut Blog', + description: SITE_INFO.description, + id: `${SITE_INFO.url}`, + link: `${SITE_INFO.url}/blog/`, + language: 'en', + image: `${SITE_INFO.openGraphImage}`, + favicon: `${SITE_INFO.favicon}`, + copyright: `All rights reserved ${new Date().getFullYear()}, ${ + SITE_INFO.title + }`, }); - }); + + for (const post of posts) { + feed.addItem({ + title: post.title, + id: `${SITE_INFO.url}/blog/${post.slug}`, + link: `${SITE_INFO.url}/blog/${post.slug}`, + description: post.description, + author: post.authors.map((author) => ({ + name: author.name, + })), + date: new Date(post.publishedAt), + image: post.coverImage || SITE_INFO.openGraphImage, + }); + } - return new Response(feed.rss2(), { - headers: { - 'Content-Type': 'text/xml', - }, - }); + return new Response(feed.rss2(), { + headers: { + 'Content-Type': 'text/xml', + 'Cache-Control': 'public, max-age=86400, stale-while-revalidate', + }, + }); + } catch (error) { + console.error('Error generating RSS feed', error); + return new Response('Internal Server Error', { status: 500 }); + } } \ No newline at end of file From 45096b7b6596da1eeb00d1aad209cd20756382e6 Mon Sep 17 00:00:00 2001 From: Dominik Koch Date: Wed, 23 Jul 2025 19:33:21 +0200 Subject: [PATCH 4/7] feat: add robots/sitemap generation --- apps/web/src/app/robots.ts | 13 ++++++++ apps/web/src/app/sitemap.ts | 61 ++++++++++++++++++++++++++++++++++ apps/web/src/constants/site.ts | 1 + 3 files changed, 75 insertions(+) create mode 100644 apps/web/src/app/robots.ts create mode 100644 apps/web/src/app/sitemap.ts create mode 100644 apps/web/src/constants/site.ts diff --git a/apps/web/src/app/robots.ts b/apps/web/src/app/robots.ts new file mode 100644 index 00000000..ad6c31c8 --- /dev/null +++ b/apps/web/src/app/robots.ts @@ -0,0 +1,13 @@ +import type { MetadataRoute } from "next"; +import { SITE_URL } from "@/constants/site"; + +export default function robots(): MetadataRoute.Robots { + return { + rules: { + userAgent: "*", + allow: "/", + disallow: ["/_next/", "/projects/", "/editor/"], + }, + sitemap: `${SITE_URL}/sitemap.xml`, + }; +} diff --git a/apps/web/src/app/sitemap.ts b/apps/web/src/app/sitemap.ts new file mode 100644 index 00000000..81a5ebf4 --- /dev/null +++ b/apps/web/src/app/sitemap.ts @@ -0,0 +1,61 @@ +import { SITE_URL } from "@/constants/site"; +import { getPosts } from "@/lib/blog-query"; +import type { MetadataRoute } from "next"; + +export default async function sitemap(): Promise { + const data = await getPosts(); + + const postPages: MetadataRoute.Sitemap = + data?.posts?.map((post) => ({ + url: `${SITE_URL}/blog/${post.slug}`, + lastModified: new Date(post.publishedAt), + changeFrequency: "weekly", + priority: 0.8, + })) ?? []; + + return [ + { + url: SITE_URL, + lastModified: new Date(), + changeFrequency: "weekly", + priority: 1, + }, + { + url: `${SITE_URL}/contributors`, + lastModified: new Date(), + changeFrequency: "daily", + priority: 0.5, + }, + { + url: `${SITE_URL}/roadmap`, + lastModified: new Date(), + changeFrequency: "weekly", + priority: 1, + }, + { + url: `${SITE_URL}/privacy`, + lastModified: new Date(), + changeFrequency: "monthly", + priority: 0.5, + }, + { + url: `${SITE_URL}/terms`, + lastModified: new Date(), + changeFrequency: "monthly", + priority: 0.5, + }, + { + url: `${SITE_URL}/why-not-capcut`, + lastModified: new Date(), + changeFrequency: "yearly", + priority: 1, + }, + { + url: `${SITE_URL}/blog`, + lastModified: new Date(), + changeFrequency: "weekly", + priority: 1, + }, + ...postPages, + ]; +} diff --git a/apps/web/src/constants/site.ts b/apps/web/src/constants/site.ts new file mode 100644 index 00000000..61ec29c1 --- /dev/null +++ b/apps/web/src/constants/site.ts @@ -0,0 +1 @@ +export const SITE_URL = "https://opencut.app"; \ No newline at end of file From 17d1336c8068b42cb0f48dad511dec41d2285bfb Mon Sep 17 00:00:00 2001 From: Dominik Koch Date: Wed, 23 Jul 2025 19:33:30 +0200 Subject: [PATCH 5/7] chore: bump nextjs --- apps/web/package.json | 2 +- bun.lock | 26 +++++++++++++++++++++++--- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/apps/web/package.json b/apps/web/package.json index c409136c..f85560a9 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -42,7 +42,7 @@ "input-otp": "^1.4.1", "lucide-react": "^0.468.0", "motion": "^12.18.1", - "next": "^15.4.1", + "next": "^15.4.3", "next-themes": "^0.4.4", "pg": "^8.16.2", "radix-ui": "^1.4.2", diff --git a/bun.lock b/bun.lock index 52cfff88..da2c6d9f 100644 --- a/bun.lock +++ b/bun.lock @@ -43,7 +43,7 @@ "input-otp": "^1.4.1", "lucide-react": "^0.468.0", "motion": "^12.18.1", - "next": "^15.4.1", + "next": "^15.4.3", "next-themes": "^0.4.4", "pg": "^8.16.2", "radix-ui": "^1.4.2", @@ -1163,8 +1163,6 @@ "@esbuild-kit/core-utils/esbuild": ["esbuild@0.18.20", "", { "optionalDependencies": { "@esbuild/android-arm": "0.18.20", "@esbuild/android-arm64": "0.18.20", "@esbuild/android-x64": "0.18.20", "@esbuild/darwin-arm64": "0.18.20", "@esbuild/darwin-x64": "0.18.20", "@esbuild/freebsd-arm64": "0.18.20", "@esbuild/freebsd-x64": "0.18.20", "@esbuild/linux-arm": "0.18.20", "@esbuild/linux-arm64": "0.18.20", "@esbuild/linux-ia32": "0.18.20", "@esbuild/linux-loong64": "0.18.20", "@esbuild/linux-mips64el": "0.18.20", "@esbuild/linux-ppc64": "0.18.20", "@esbuild/linux-riscv64": "0.18.20", "@esbuild/linux-s390x": "0.18.20", "@esbuild/linux-x64": "0.18.20", "@esbuild/netbsd-x64": "0.18.20", "@esbuild/openbsd-x64": "0.18.20", "@esbuild/sunos-x64": "0.18.20", "@esbuild/win32-arm64": "0.18.20", "@esbuild/win32-ia32": "0.18.20", "@esbuild/win32-x64": "0.18.20" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA=="], - "@types/bun/bun-types": ["bun-types@1.2.19", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, "sha512-uAOTaZSPuYsWIXRpj7o56Let0g/wjihKCkeRqUBhlLVM/Bt+Fj9xTo+LhC1OV1XDaGkz4hNC80et5xgy+9KTHQ=="], - "better-auth/zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="], "chokidar/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], @@ -1175,6 +1173,8 @@ "next/postcss": ["postcss@8.4.31", "", { "dependencies": { "nanoid": "^3.3.6", "picocolors": "^1.0.0", "source-map-js": "^1.0.2" } }, "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ=="], + "opencut/next": ["next@15.4.3", "", { "dependencies": { "@next/env": "15.4.3", "@swc/helpers": "0.5.15", "caniuse-lite": "^1.0.30001579", "postcss": "8.4.31", "styled-jsx": "5.1.6" }, "optionalDependencies": { "@next/swc-darwin-arm64": "15.4.3", "@next/swc-darwin-x64": "15.4.3", "@next/swc-linux-arm64-gnu": "15.4.3", "@next/swc-linux-arm64-musl": "15.4.3", "@next/swc-linux-x64-gnu": "15.4.3", "@next/swc-linux-x64-musl": "15.4.3", "@next/swc-win32-arm64-msvc": "15.4.3", "@next/swc-win32-x64-msvc": "15.4.3", "sharp": "^0.34.3" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", "@playwright/test": "^1.51.1", "babel-plugin-react-compiler": "*", "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "sass": "^1.3.0" }, "optionalPeers": ["@opentelemetry/api", "@playwright/test", "babel-plugin-react-compiler", "sass"], "bin": { "next": "dist/bin/next" } }, "sha512-uW7Qe6poVasNIE1X382nI29oxSdFJzjQzTgJFLD43MxyPfGKKxCMySllhBpvqr48f58Om+tLMivzRwBpXEytvA=="], + "opencut/zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="], "parse-entities/@types/unist": ["@types/unist@2.0.11", "", {}, "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA=="], @@ -1245,6 +1245,26 @@ "motion/framer-motion/motion-utils": ["motion-utils@12.23.6", "", {}, "sha512-eAWoPgr4eFEOFfg2WjIsMoqJTW6Z8MTUCgn/GZ3VRpClWBdnbjryiA3ZSNLyxCTmCQx4RmYX6jX1iWHbenUPNQ=="], + "opencut/next/@next/env": ["@next/env@15.4.3", "", {}, "sha512-lKJ9KJAvaWzqurIsz6NWdQOLj96mdhuDMusLSYHw9HBe2On7BjUwU1WeRvq19x7NrEK3iOgMeSBV5qEhVH1cMw=="], + + "opencut/next/@next/swc-darwin-arm64": ["@next/swc-darwin-arm64@15.4.3", "", { "os": "darwin", "cpu": "arm64" }, "sha512-YAhZWKeEYY7LHQJiQ8fe3Y6ymfcDcTn7rDC8PDu/pdeIl1Z2LHD4uyPNuQUGCEQT//MSNv6oZCeQzZfTCKZv+A=="], + + "opencut/next/@next/swc-darwin-x64": ["@next/swc-darwin-x64@15.4.3", "", { "os": "darwin", "cpu": "x64" }, "sha512-ZPHRdd51xaxCMpT4viQ6h8TgYM1zPW1JIeksPY9wKlyvBVUQqrWqw8kEh1sa7/x0Ied+U7pYHkAkutrUwxbMcg=="], + + "opencut/next/@next/swc-linux-arm64-gnu": ["@next/swc-linux-arm64-gnu@15.4.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-QUdqftCXC5vw5cowucqi9FeOPQ0vdMxoOHLY0J5jPdercwSJFjdi9CkEO4Xkq1eG4t1TB/BG81n6rmTsWoILnw=="], + + "opencut/next/@next/swc-linux-arm64-musl": ["@next/swc-linux-arm64-musl@15.4.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-HTL31NsmoafX+r5g91Yj3+q34nrn1xKmCWVuNA+fUWO4X0pr+n83uGzLyEOn0kUqbMZ40KmWx+4wsbMoUChkiQ=="], + + "opencut/next/@next/swc-linux-x64-gnu": ["@next/swc-linux-x64-gnu@15.4.3", "", { "os": "linux", "cpu": "x64" }, "sha512-HRQLWoeFkKXd2YCEEy9GhfwOijRm37x4w5r0MMVHxBKSA6ms3JoPUXvGhfHT6srnGRcEUWNrQ2vzkHir5ZWTSw=="], + + "opencut/next/@next/swc-linux-x64-musl": ["@next/swc-linux-x64-musl@15.4.3", "", { "os": "linux", "cpu": "x64" }, "sha512-NyXUx6G7AayaRGUsVPenuwhyAoyxjQuQPaK50AXoaAHPwRuif4WmSrXUs8/Y0HJIZh8E/YXRm9H7uuGfiacpuQ=="], + + "opencut/next/@next/swc-win32-arm64-msvc": ["@next/swc-win32-arm64-msvc@15.4.3", "", { "os": "win32", "cpu": "arm64" }, "sha512-2CUTmpzN/7cL1a7GjdLkDFlfH3nwMwW8a6JiaAUsL9MtKmNNO3fnXqnY0Zk30fii3hVEl4dr7ztrpYt0t2CcGQ=="], + + "opencut/next/@next/swc-win32-x64-msvc": ["@next/swc-win32-x64-msvc@15.4.3", "", { "os": "win32", "cpu": "x64" }, "sha512-i54YgUhvrUQxQD84SjAbkfWhYkOdm/DNRAVekCHLWxVg3aUbyC6NFQn9TwgCkX5QAS2pXCJo3kFboSFvrsd7dA=="], + + "opencut/next/postcss": ["postcss@8.4.31", "", { "dependencies": { "nanoid": "^3.3.6", "picocolors": "^1.0.0", "source-map-js": "^1.0.2" } }, "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ=="], + "string-width-cjs/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], "wrap-ansi-cjs/string-width/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], From 275a23f583ca8da6aa17e1df361818b558baf984 Mon Sep 17 00:00:00 2001 From: Dominik Koch Date: Thu, 24 Jul 2025 13:35:06 +0200 Subject: [PATCH 6/7] fix: clean up code and add canonical url --- apps/web/src/app/metadata.ts | 21 ++++++--------------- apps/web/src/app/page.tsx | 8 ++++++++ apps/web/src/constants/site.ts | 12 +++++++++++- 3 files changed, 25 insertions(+), 16 deletions(-) diff --git a/apps/web/src/app/metadata.ts b/apps/web/src/app/metadata.ts index de8cf6f0..0608974d 100644 --- a/apps/web/src/app/metadata.ts +++ b/apps/web/src/app/metadata.ts @@ -1,24 +1,15 @@ -import { Metadata } from "next"; - -export const SITE_INFO = { - title: "OpenCut", - description: - "A simple but powerful video editor that gets the job done. In your browser.", - url: "https://opencut.app", - openGraphImage: "https://opencut.app/open-graph/default.jpg", - twitterImage: "/open-graph/default.jpg", - favicon: "/favicon.ico", -}; +import type { Metadata } from "next"; +import { SITE_INFO, SITE_URL } from "@/constants/site"; export const baseMetaData: Metadata = { - metadataBase: new URL("https://opencut.app"), + metadataBase: new URL(SITE_URL), title: SITE_INFO.title, description: SITE_INFO.description, openGraph: { title: SITE_INFO.title, description: SITE_INFO.description, - url: "https://opencut.app", - siteName: "OpenCut", + url: SITE_URL, + siteName: SITE_INFO.title, locale: "en_US", type: "website", images: [ @@ -26,7 +17,7 @@ export const baseMetaData: Metadata = { url: SITE_INFO.openGraphImage, width: 1200, height: 630, - alt: "OpenCut", + alt: "OpenCut Wordmark", }, ], }, diff --git a/apps/web/src/app/page.tsx b/apps/web/src/app/page.tsx index 74961549..818884ed 100644 --- a/apps/web/src/app/page.tsx +++ b/apps/web/src/app/page.tsx @@ -1,6 +1,14 @@ import { Hero } from "@/components/landing/hero"; import { Header } from "@/components/header"; import { Footer } from "@/components/footer"; +import type { Metadata } from "next"; +import { SITE_URL } from "@/constants/site"; + +export const metadata: Metadata = { + alternates: { + canonical: SITE_URL, + }, +}; export default async function Home() { return ( diff --git a/apps/web/src/constants/site.ts b/apps/web/src/constants/site.ts index 61ec29c1..efbc2b58 100644 --- a/apps/web/src/constants/site.ts +++ b/apps/web/src/constants/site.ts @@ -1 +1,11 @@ -export const SITE_URL = "https://opencut.app"; \ No newline at end of file +export const SITE_URL = "https://opencut.app"; + +export const SITE_INFO = { + title: "OpenCut", + description: + "A simple but powerful video editor that gets the job done. In your browser.", + url: SITE_URL, + openGraphImage: "/open-graph/default.jpg", + twitterImage: "/open-graph/default.jpg", + favicon: "/favicon.ico", + }; \ No newline at end of file From cf039e48a94c5ab17c1193f02705e9f8baa370c6 Mon Sep 17 00:00:00 2001 From: Dominik Koch Date: Thu, 24 Jul 2025 13:44:03 +0200 Subject: [PATCH 7/7] fix: import site info from correct source --- apps/web/src/app/rss.xml/route.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/apps/web/src/app/rss.xml/route.ts b/apps/web/src/app/rss.xml/route.ts index b16bd856..1ed63994 100644 --- a/apps/web/src/app/rss.xml/route.ts +++ b/apps/web/src/app/rss.xml/route.ts @@ -1,16 +1,16 @@ import { Feed } from 'feed'; import { getPosts } from '@/lib/blog-query'; -import { SITE_INFO } from '../metadata'; +import { SITE_INFO, SITE_URL } from '@/constants/site'; export async function GET() { try { const { posts } = await getPosts(); const feed = new Feed({ - title: 'OpenCut Blog', + title: `${SITE_INFO.title} Blog`, description: SITE_INFO.description, - id: `${SITE_INFO.url}`, - link: `${SITE_INFO.url}/blog/`, + id: `${SITE_URL}`, + link: `${SITE_URL}/blog/`, language: 'en', image: `${SITE_INFO.openGraphImage}`, favicon: `${SITE_INFO.favicon}`, @@ -22,8 +22,8 @@ export async function GET() { for (const post of posts) { feed.addItem({ title: post.title, - id: `${SITE_INFO.url}/blog/${post.slug}`, - link: `${SITE_INFO.url}/blog/${post.slug}`, + id: `${SITE_URL}/blog/${post.slug}`, + link: `${SITE_URL}/blog/${post.slug}`, description: post.description, author: post.authors.map((author) => ({ name: author.name,