mirror of
https://github.com/complexcaresolutions/cms.c2sgmbh.git
synced 2026-03-17 17:24:12 +00:00
- Fix slug-validation.ts: Use proper Where type from Payload - Fix processFeaturedVideo.ts: Remove TypeWithID constraint, use type casting - Fix retention-worker.ts: Remove unused import cleanupExpiredConsentLogs 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
160 lines
3.8 KiB
TypeScript
160 lines
3.8 KiB
TypeScript
/**
|
|
* Slug Validation Utilities
|
|
*
|
|
* Stellt sicher, dass Slugs innerhalb eines Tenants eindeutig sind.
|
|
*/
|
|
|
|
import type { Payload, Where } from 'payload'
|
|
import type { Config } from '@/payload-types'
|
|
|
|
type CollectionSlug = keyof Config['collections']
|
|
type LocaleType = 'de' | 'en' | 'all' | undefined
|
|
|
|
export interface SlugValidationOptions {
|
|
/** Collection slug */
|
|
collection: CollectionSlug
|
|
/** Field name for slug (default: 'slug') */
|
|
slugField?: string
|
|
/** Field name for tenant (default: 'tenant') */
|
|
tenantField?: string
|
|
/** Whether to check per locale (default: false) */
|
|
perLocale?: boolean
|
|
}
|
|
|
|
/**
|
|
* Validates that a slug is unique within a tenant
|
|
*
|
|
* @throws Error if slug already exists for this tenant
|
|
*/
|
|
export async function validateUniqueSlug(
|
|
payload: Payload,
|
|
data: Record<string, unknown>,
|
|
options: SlugValidationOptions & {
|
|
existingId?: number | string
|
|
locale?: string
|
|
}
|
|
): Promise<void> {
|
|
const {
|
|
collection,
|
|
slugField = 'slug',
|
|
tenantField = 'tenant',
|
|
perLocale = false,
|
|
existingId,
|
|
locale,
|
|
} = options
|
|
|
|
const slug = data[slugField]
|
|
const tenantId = data[tenantField]
|
|
|
|
// Skip if no slug provided
|
|
if (!slug || typeof slug !== 'string') {
|
|
return
|
|
}
|
|
|
|
// Build where clause
|
|
const conditions: Where[] = [{ [slugField]: { equals: slug } }]
|
|
|
|
// Add tenant filter if tenant is set
|
|
if (tenantId) {
|
|
conditions.push({ [tenantField]: { equals: tenantId } })
|
|
}
|
|
|
|
// Exclude current document when updating
|
|
if (existingId) {
|
|
conditions.push({ id: { not_equals: existingId } })
|
|
}
|
|
|
|
const where: Where = conditions.length > 1 ? { and: conditions } : conditions[0]
|
|
|
|
// Determine locale for query
|
|
const queryLocale: LocaleType = perLocale && locale ? (locale as LocaleType) : undefined
|
|
|
|
// Check for existing documents with same slug
|
|
const existing = await payload.find({
|
|
collection,
|
|
where,
|
|
limit: 1,
|
|
depth: 0,
|
|
locale: queryLocale,
|
|
})
|
|
|
|
if (existing.totalDocs > 0) {
|
|
const tenantInfo = tenantId ? ` für diesen Tenant` : ''
|
|
throw new Error(`Der Slug "${slug}" existiert bereits${tenantInfo}. Bitte wählen Sie einen anderen.`)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Creates a beforeValidate hook for slug uniqueness
|
|
*/
|
|
export function createSlugValidationHook(options: SlugValidationOptions) {
|
|
return async ({
|
|
data,
|
|
req,
|
|
operation,
|
|
originalDoc,
|
|
}: {
|
|
data?: Record<string, unknown>
|
|
req: { payload: Payload; locale?: string }
|
|
operation: 'create' | 'update'
|
|
originalDoc?: { id?: number | string }
|
|
}) => {
|
|
if (!data) return data
|
|
|
|
await validateUniqueSlug(req.payload, data, {
|
|
...options,
|
|
existingId: operation === 'update' ? originalDoc?.id : undefined,
|
|
locale: req.locale,
|
|
})
|
|
|
|
return data
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Generates a unique slug by appending a number if necessary
|
|
*/
|
|
export async function generateUniqueSlug(
|
|
payload: Payload,
|
|
baseSlug: string,
|
|
options: SlugValidationOptions & {
|
|
existingId?: number | string
|
|
tenantId?: number | string
|
|
}
|
|
): Promise<string> {
|
|
const { collection, slugField = 'slug', tenantField = 'tenant', existingId, tenantId } = options
|
|
|
|
let slug = baseSlug
|
|
let counter = 1
|
|
let isUnique = false
|
|
|
|
while (!isUnique && counter < 100) {
|
|
const conditions: Where[] = [{ [slugField]: { equals: slug } }]
|
|
|
|
if (tenantId) {
|
|
conditions.push({ [tenantField]: { equals: tenantId } })
|
|
}
|
|
|
|
if (existingId) {
|
|
conditions.push({ id: { not_equals: existingId } })
|
|
}
|
|
|
|
const where: Where = conditions.length > 1 ? { and: conditions } : conditions[0]
|
|
|
|
const existing = await payload.find({
|
|
collection,
|
|
where,
|
|
limit: 1,
|
|
depth: 0,
|
|
})
|
|
|
|
if (existing.totalDocs === 0) {
|
|
isUnique = true
|
|
} else {
|
|
slug = `${baseSlug}-${counter}`
|
|
counter++
|
|
}
|
|
}
|
|
|
|
return slug
|
|
}
|