summaryrefslogtreecommitdiff
path: root/apps
diff options
context:
space:
mode:
authorKha Nguyen <hi@khanguyen.com>2025-07-23 23:47:18 -0500
committerKha Nguyen <hi@khanguyen.com>2025-07-23 23:47:18 -0500
commite8dc0f62daf570a9f4b183dce593e0ddb9a30774 (patch)
treed3d53425705508fb3595bec7e4328eeba6804d4b /apps
parent92079777b9c3f8f157163124fa5b02386391cc2f (diff)
parent81ad5c1782bfed92c2fdcd78b5131748871f098e (diff)
downloadopencut-e8dc0f62daf570a9f4b183dce593e0ddb9a30774.tar.gz
Merge branch 'main' into fix/improve-shortcuts
Diffstat (limited to 'apps')
-rw-r--r--apps/web/package.json3
-rw-r--r--apps/web/src/app/metadata.ts32
-rw-r--r--apps/web/src/app/robots.ts13
-rw-r--r--apps/web/src/app/rss.xml/route.ts46
-rw-r--r--apps/web/src/app/sitemap.ts61
-rw-r--r--apps/web/src/constants/site.ts1
6 files changed, 141 insertions, 15 deletions
diff --git a/apps/web/package.json b/apps/web/package.json
index c409136c..f5cd7da4 100644
--- a/apps/web/package.json
+++ b/apps/web/package.json
@@ -38,11 +38,12 @@
"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",
"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/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/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/rss.xml/route.ts b/apps/web/src/app/rss.xml/route.ts
new file mode 100644
index 00000000..b16bd856
--- /dev/null
+++ b/apps/web/src/app/rss.xml/route.ts
@@ -0,0 +1,46 @@
+import { Feed } from 'feed';
+import { getPosts } from '@/lib/blog-query';
+import { SITE_INFO } from '../metadata';
+
+export async function GET() {
+ 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',
+ '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
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<MetadataRoute.Sitemap> {
+ 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