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:
Alireza Hassani
2026-09-02 23:49:10 +03:30
co-authored by Cursor
parent b2a0fe73bc
commit fc4fea89f1
3 changed files with 69 additions and 30 deletions
-15
View File
@@ -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));
}
-15
View File
@@ -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));
}
+69
View File
@@ -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*"],
};