Fix production 500 caused by conflicting dynamic product/blog routes.
Remove legacy [slug] pages that clashed with [id]/[slug] and handle old URLs via middleware redirects instead. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
co-authored by
Cursor
parent
b2a0fe73bc
commit
fc4fea89f1
@@ -1,15 +0,0 @@
|
|||||||
import { notFound, permanentRedirect } from "next/navigation";
|
|
||||||
import { getPost } from "@/lib/meshkee";
|
|
||||||
import { blogPath } from "@/lib/urls";
|
|
||||||
|
|
||||||
/** Legacy /blog/{slug} → canonical /blog/{id}/{titleSlug} */
|
|
||||||
export default async function LegacyBlogRedirect({
|
|
||||||
params,
|
|
||||||
}: {
|
|
||||||
params: Promise<{ slug: string }>;
|
|
||||||
}) {
|
|
||||||
const { slug } = await params;
|
|
||||||
const post = await getPost(slug);
|
|
||||||
if (!post) notFound();
|
|
||||||
permanentRedirect(blogPath(post));
|
|
||||||
}
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
import { notFound, permanentRedirect } from "next/navigation";
|
|
||||||
import { getProduct } from "@/lib/meshkee";
|
|
||||||
import { productPath } from "@/lib/urls";
|
|
||||||
|
|
||||||
/** Legacy /products/{slug} → canonical /products/{id}/{nameFaSlug} */
|
|
||||||
export default async function LegacyProductRedirect({
|
|
||||||
params,
|
|
||||||
}: {
|
|
||||||
params: Promise<{ slug: string }>;
|
|
||||||
}) {
|
|
||||||
const { slug } = await params;
|
|
||||||
const product = await getProduct(slug);
|
|
||||||
if (!product) notFound();
|
|
||||||
permanentRedirect(productPath(product));
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
import { NextResponse, type NextRequest } from "next/server";
|
||||||
|
import { slugifyFa } from "@/lib/slugify";
|
||||||
|
import { TENANT_DOMAIN } from "@/lib/meshkee";
|
||||||
|
|
||||||
|
const API_BASE = "https://api.meshkee.com/api/v1";
|
||||||
|
|
||||||
|
function canonicalProductPath(product: { id: string; nameFa: string }) {
|
||||||
|
return `/products/${product.id}/${encodeURIComponent(slugifyFa(product.nameFa))}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function canonicalBlogPath(post: { id: string; nameFa?: string; titleFa?: string; title?: string }) {
|
||||||
|
const title = post.nameFa || post.titleFa || post.title || "post";
|
||||||
|
return `/blog/${post.id}/${encodeURIComponent(slugifyFa(title))}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function redirectLegacyProduct(slug: string, request: NextRequest) {
|
||||||
|
const res = await fetch(
|
||||||
|
`${API_BASE}/tenants/${TENANT_DOMAIN}/products/${encodeURIComponent(slug)}`,
|
||||||
|
{ next: { revalidate: 300 } },
|
||||||
|
);
|
||||||
|
if (!res.ok) return null;
|
||||||
|
const data = (await res.json()) as { product?: { id: string; nameFa: string } };
|
||||||
|
const product = data.product;
|
||||||
|
if (!product) return null;
|
||||||
|
return NextResponse.redirect(new URL(canonicalProductPath(product), request.url), 308);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function redirectLegacyBlog(slug: string, request: NextRequest) {
|
||||||
|
const res = await fetch(
|
||||||
|
`${API_BASE}/tenants/${TENANT_DOMAIN}/blogs/${encodeURIComponent(slug)}`,
|
||||||
|
{ next: { revalidate: 300 } },
|
||||||
|
);
|
||||||
|
if (!res.ok) return null;
|
||||||
|
const data = (await res.json()) as {
|
||||||
|
blog?: { id: string; nameFa?: string; titleFa?: string; title?: string };
|
||||||
|
post?: { id: string; nameFa?: string; titleFa?: string; title?: string };
|
||||||
|
};
|
||||||
|
const post = data.blog ?? data.post;
|
||||||
|
if (!post) return null;
|
||||||
|
return NextResponse.redirect(new URL(canonicalBlogPath(post), request.url), 308);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function middleware(request: NextRequest) {
|
||||||
|
const { pathname } = request.nextUrl;
|
||||||
|
|
||||||
|
const productMatch = pathname.match(/^\/products\/([^/]+)$/);
|
||||||
|
if (productMatch) {
|
||||||
|
const segment = decodeURIComponent(productMatch[1]);
|
||||||
|
if (segment !== "category" && !/^\d+$/.test(segment)) {
|
||||||
|
const redirect = await redirectLegacyProduct(segment, request);
|
||||||
|
if (redirect) return redirect;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const blogMatch = pathname.match(/^\/blog\/([^/]+)$/);
|
||||||
|
if (blogMatch) {
|
||||||
|
const segment = decodeURIComponent(blogMatch[1]);
|
||||||
|
if (!/^\d+$/.test(segment)) {
|
||||||
|
const redirect = await redirectLegacyBlog(segment, request);
|
||||||
|
if (redirect) return redirect;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NextResponse.next();
|
||||||
|
}
|
||||||
|
|
||||||
|
export const config = {
|
||||||
|
matcher: ["/products/:path*", "/blog/:path*"],
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user