phase 1-2 done
This commit is contained in:
90
src/app/api/og/route.tsx
Normal file
90
src/app/api/og/route.tsx
Normal file
@@ -0,0 +1,90 @@
|
||||
import { ImageResponse } from "next/og";
|
||||
import type { NextRequest } from "next/server";
|
||||
|
||||
export const runtime = "edge";
|
||||
|
||||
export async function GET(req: NextRequest) {
|
||||
const { searchParams } = new URL(req.url);
|
||||
const title = searchParams.get("title") ?? "Ali Taghavi";
|
||||
const category = searchParams.get("category") ?? "";
|
||||
|
||||
return new ImageResponse(
|
||||
(
|
||||
<div
|
||||
style={{
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "flex-end",
|
||||
padding: "60px",
|
||||
background: "#0A0A0B",
|
||||
fontFamily: "sans-serif",
|
||||
position: "relative",
|
||||
}}
|
||||
>
|
||||
{/* Dot grid */}
|
||||
<div
|
||||
style={{
|
||||
position: "absolute",
|
||||
inset: 0,
|
||||
backgroundImage: "radial-gradient(circle, rgba(255,255,255,0.04) 1px, transparent 1px)",
|
||||
backgroundSize: "28px 28px",
|
||||
}}
|
||||
/>
|
||||
{/* Accent glow */}
|
||||
<div
|
||||
style={{
|
||||
position: "absolute",
|
||||
top: "-100px",
|
||||
right: "-100px",
|
||||
width: "400px",
|
||||
height: "400px",
|
||||
borderRadius: "50%",
|
||||
background: "rgba(204,253,101,0.07)",
|
||||
filter: "blur(60px)",
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* Category label */}
|
||||
{category && (
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: "8px",
|
||||
marginBottom: "20px",
|
||||
}}
|
||||
>
|
||||
<span style={{ color: "#CCFD65", fontSize: "14px", fontFamily: "monospace" }}>
|
||||
// {category}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Title */}
|
||||
<div
|
||||
style={{
|
||||
fontSize: title.length > 60 ? "36px" : "48px",
|
||||
fontWeight: "700",
|
||||
color: "#FAFAFA",
|
||||
lineHeight: 1.3,
|
||||
maxWidth: "900px",
|
||||
marginBottom: "32px",
|
||||
}}
|
||||
>
|
||||
{title}
|
||||
</div>
|
||||
|
||||
{/* Footer */}
|
||||
<div style={{ display: "flex", alignItems: "center", justifyContent: "space-between" }}>
|
||||
<span style={{ color: "#CCFD65", fontSize: "18px", fontFamily: "monospace", fontWeight: "700" }}>
|
||||
// ali taghavi
|
||||
</span>
|
||||
<span style={{ color: "#A1A1AA", fontSize: "14px" }}>biztaghavi.com</span>
|
||||
</div>
|
||||
</div>
|
||||
),
|
||||
{ width: 1200, height: 630 }
|
||||
);
|
||||
}
|
||||
13
src/app/api/revalidate/route.ts
Normal file
13
src/app/api/revalidate/route.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import { revalidateTag } from "next/cache";
|
||||
import type { NextRequest } from "next/server";
|
||||
|
||||
export async function POST(req: NextRequest) {
|
||||
const secret = req.nextUrl.searchParams.get("secret");
|
||||
|
||||
if (secret !== process.env.REVALIDATE_SECRET) {
|
||||
return Response.json({ message: "Invalid secret" }, { status: 401 });
|
||||
}
|
||||
|
||||
revalidateTag("sanity", "default");
|
||||
return Response.json({ revalidated: true, now: Date.now() });
|
||||
}
|
||||
45
src/app/api/rss/route.ts
Normal file
45
src/app/api/rss/route.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
import { sanityFetch } from "@/lib/sanity/client";
|
||||
import { rssPostsQuery } from "@/lib/sanity/queries";
|
||||
|
||||
const SITE_URL = "https://biztaghavi.com";
|
||||
|
||||
export async function GET() {
|
||||
const posts = await sanityFetch<
|
||||
Array<{ _id: string; title: string; slug: { current: string }; excerpt?: string; publishedAt: string; category: string }>
|
||||
>(rssPostsQuery);
|
||||
|
||||
const items = (posts ?? [])
|
||||
.map((post) => {
|
||||
const url = `${SITE_URL}/writing/${post.slug.current}`;
|
||||
const date = post.publishedAt ? new Date(post.publishedAt).toUTCString() : "";
|
||||
return `
|
||||
<item>
|
||||
<title><![CDATA[${post.title}]]></title>
|
||||
<link>${url}</link>
|
||||
<guid>${url}</guid>
|
||||
<pubDate>${date}</pubDate>
|
||||
<category>${post.category}</category>
|
||||
${post.excerpt ? `<description><![CDATA[${post.excerpt}]]></description>` : ""}
|
||||
</item>`;
|
||||
})
|
||||
.join("\n");
|
||||
|
||||
const xml = `<?xml version="1.0" encoding="UTF-8"?>
|
||||
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
|
||||
<channel>
|
||||
<title>علی تقوی — نوشتهها</title>
|
||||
<link>${SITE_URL}</link>
|
||||
<description>یادداشتها، تحلیلها و آموختههایم از ساختن کسبوکار، محصول و برند</description>
|
||||
<language>fa</language>
|
||||
<atom:link href="${SITE_URL}/api/rss" rel="self" type="application/rss+xml" />
|
||||
${items}
|
||||
</channel>
|
||||
</rss>`;
|
||||
|
||||
return new Response(xml, {
|
||||
headers: {
|
||||
"Content-Type": "application/rss+xml; charset=utf-8",
|
||||
"Cache-Control": "public, max-age=3600, s-maxage=3600",
|
||||
},
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user