mirror of
https://github.com/complexcaresolutions/cms.c2sgmbh.git
synced 2026-03-17 16:14:12 +00:00
fix: support slug and where[] query params in custom posts route
The custom /api/posts route intercepted all post queries but only supported listing parameters (category, type, page). Frontend detail pages sending where[slug][equals]=X got all posts back, always showing the latest post regardless of which article was clicked. Now parses slug from both ?slug=X and ?where[slug][equals]=X format. Replaced getPostsByCategory with direct payload.find using properly typed Where conditions. Detail queries (with slug) include content and readingTime in the response. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
6d13361ad4
commit
9e791648e9
1 changed files with 76 additions and 40 deletions
|
|
@ -3,8 +3,8 @@
|
||||||
|
|
||||||
import { NextRequest, NextResponse } from 'next/server'
|
import { NextRequest, NextResponse } from 'next/server'
|
||||||
import { getPayload } from 'payload'
|
import { getPayload } from 'payload'
|
||||||
|
import type { Where } from 'payload'
|
||||||
import config from '@payload-config'
|
import config from '@payload-config'
|
||||||
import { getPostsByCategory } from '@/lib/search'
|
|
||||||
import type { Category } from '@/payload-types'
|
import type { Category } from '@/payload-types'
|
||||||
import {
|
import {
|
||||||
searchLimiter,
|
searchLimiter,
|
||||||
|
|
@ -42,11 +42,12 @@ export async function GET(request: NextRequest) {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse query parameters
|
// Parse query parameters (supports both ?key=val and ?where[key][equals]=val formats)
|
||||||
const { searchParams } = new URL(request.url)
|
const { searchParams } = new URL(request.url)
|
||||||
const category = searchParams.get('category')?.trim()
|
const category = searchParams.get('category')?.trim()
|
||||||
const type = searchParams.get('type')?.trim() as 'blog' | 'news' | 'press' | 'announcement' | undefined
|
const type = searchParams.get('type')?.trim() as 'blog' | 'news' | 'press' | 'announcement' | undefined
|
||||||
const tenantParam = searchParams.get('tenant') || searchParams.get('where[tenant][equals]')
|
const tenantParam = searchParams.get('tenant') || searchParams.get('where[tenant][equals]')
|
||||||
|
const slugParam = searchParams.get('slug')?.trim() || searchParams.get('where[slug][equals]')?.trim()
|
||||||
const pageParam = searchParams.get('page')
|
const pageParam = searchParams.get('page')
|
||||||
const limitParam = searchParams.get('limit')
|
const limitParam = searchParams.get('limit')
|
||||||
const localeParam = searchParams.get('locale')?.trim()
|
const localeParam = searchParams.get('locale')?.trim()
|
||||||
|
|
@ -90,47 +91,82 @@ export async function GET(request: NextRequest) {
|
||||||
// Get payload instance
|
// Get payload instance
|
||||||
const payload = await getPayload({ config })
|
const payload = await getPayload({ config })
|
||||||
|
|
||||||
// Get posts
|
// Build query: slug lookup uses direct payload.find for full where support
|
||||||
const result = await getPostsByCategory(payload, {
|
const whereConditions: Where[] = [
|
||||||
tenantId,
|
{ tenant: { equals: tenantId } },
|
||||||
categorySlug: category,
|
{ status: { equals: 'published' } },
|
||||||
type,
|
]
|
||||||
locale,
|
if (slugParam) {
|
||||||
|
whereConditions.push({ slug: { equals: slugParam } })
|
||||||
|
}
|
||||||
|
if (type) {
|
||||||
|
whereConditions.push({ type: { equals: type } })
|
||||||
|
}
|
||||||
|
if (category) {
|
||||||
|
// Category filter by slug: look up category first
|
||||||
|
const catResult = await payload.find({
|
||||||
|
collection: 'categories',
|
||||||
|
where: {
|
||||||
|
slug: { equals: category },
|
||||||
|
...(tenantId ? { tenant: { equals: tenantId } } : {}),
|
||||||
|
},
|
||||||
|
locale: locale as 'de' | 'en',
|
||||||
|
limit: 1,
|
||||||
|
})
|
||||||
|
if (catResult.docs.length > 0) {
|
||||||
|
whereConditions.push({ categories: { contains: catResult.docs[0].id } })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await payload.find({
|
||||||
|
collection: 'posts',
|
||||||
|
where: { and: whereConditions },
|
||||||
|
locale: locale as 'de' | 'en',
|
||||||
|
fallbackLocale: 'de',
|
||||||
page,
|
page,
|
||||||
limit,
|
limit,
|
||||||
|
sort: '-publishedAt',
|
||||||
|
depth: slugParam ? 2 : 1,
|
||||||
})
|
})
|
||||||
|
|
||||||
// Transform response
|
// Transform response — slug lookup returns full post, listing returns summary
|
||||||
const response = {
|
const response = {
|
||||||
docs: result.docs.map((post) => ({
|
docs: result.docs.map((post) => {
|
||||||
id: post.id,
|
const base = {
|
||||||
title: post.title,
|
id: post.id,
|
||||||
slug: post.slug,
|
title: post.title,
|
||||||
excerpt: post.excerpt || null,
|
slug: post.slug,
|
||||||
publishedAt: post.publishedAt || null,
|
excerpt: post.excerpt || null,
|
||||||
type: (post as typeof post & { type?: string }).type || 'blog',
|
publishedAt: post.publishedAt || null,
|
||||||
featuredImage: post.featuredImage && typeof post.featuredImage === 'object'
|
type: (post as typeof post & { type?: string }).type || 'blog',
|
||||||
? {
|
featuredImage: post.featuredImage && typeof post.featuredImage === 'object'
|
||||||
url: post.featuredImage.url,
|
? {
|
||||||
alt: post.featuredImage.alt,
|
url: post.featuredImage.url,
|
||||||
width: post.featuredImage.width,
|
alt: post.featuredImage.alt,
|
||||||
height: post.featuredImage.height,
|
width: post.featuredImage.width,
|
||||||
}
|
height: post.featuredImage.height,
|
||||||
: null,
|
}
|
||||||
category: Array.isArray(post.categories) && post.categories.length > 0
|
: null,
|
||||||
? (() => {
|
category: Array.isArray(post.categories) && post.categories.length > 0
|
||||||
const firstCat = post.categories.find(
|
? (() => {
|
||||||
(cat): cat is Category => cat !== null && typeof cat === 'object' && 'name' in cat
|
const firstCat = post.categories.find(
|
||||||
)
|
(cat): cat is Category => cat !== null && typeof cat === 'object' && 'name' in cat
|
||||||
return firstCat ? { name: firstCat.name, slug: firstCat.slug } : null
|
)
|
||||||
})()
|
return firstCat ? { name: firstCat.name, slug: firstCat.slug } : null
|
||||||
: null,
|
})()
|
||||||
categories: Array.isArray(post.categories)
|
: null,
|
||||||
? post.categories
|
categories: Array.isArray(post.categories)
|
||||||
.filter((cat): cat is Category => cat !== null && typeof cat === 'object' && 'name' in cat)
|
? post.categories
|
||||||
.map((cat) => ({ name: cat.name, slug: cat.slug }))
|
.filter((cat): cat is Category => cat !== null && typeof cat === 'object' && 'name' in cat)
|
||||||
: [],
|
.map((cat) => ({ name: cat.name, slug: cat.slug }))
|
||||||
})),
|
: [],
|
||||||
|
}
|
||||||
|
// Include content and extra fields for detail queries (slug lookup)
|
||||||
|
if (slugParam) {
|
||||||
|
return { ...base, content: post.content, readingTime: (post as typeof post & { readingTime?: number }).readingTime }
|
||||||
|
}
|
||||||
|
return base
|
||||||
|
}),
|
||||||
pagination: {
|
pagination: {
|
||||||
page: result.page,
|
page: result.page,
|
||||||
limit,
|
limit,
|
||||||
|
|
@ -140,8 +176,8 @@ export async function GET(request: NextRequest) {
|
||||||
hasPrevPage: result.hasPrevPage,
|
hasPrevPage: result.hasPrevPage,
|
||||||
},
|
},
|
||||||
filters: {
|
filters: {
|
||||||
category,
|
...(category ? { category } : {}),
|
||||||
type,
|
...(type ? { type } : {}),
|
||||||
locale,
|
locale,
|
||||||
tenant: tenantId,
|
tenant: tenantId,
|
||||||
},
|
},
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue