fix(seed-forms): tighten auth guard + per-form idempotency to handle partial failures
Some checks are pending
CI / Type Check (push) Waiting to run
CI / Lint (push) Waiting to run
CI / Unit Tests (push) Waiting to run
Deploy / Build & Push Image (push) Waiting to run
Deploy / Deploy to VPS (push) Blocked by required conditions

- Fix auth bypass when SYNC_SECRET env var is unset: now defaults to empty string
- Change idempotency check from totalDocs >= 2 to per-form existence checks
- Allow reuse of existing forms instead of creating duplicates if one creation fails
- Handles partial seed state gracefully (e.g., birthday form exists but group form missing)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Vadym Samoilenko 2026-06-03 13:59:06 +01:00
parent 65498e2da5
commit b5c34f6f0b

View file

@ -5,142 +5,154 @@ import config from '@payload-config'
export async function POST(req: NextRequest): Promise<NextResponse> {
const secret = req.nextUrl.searchParams.get('secret')
if (!secret || secret !== process.env['SYNC_SECRET']) {
const SYNC_SECRET = process.env['SYNC_SECRET'] ?? ''
if (!SYNC_SECRET || secret !== SYNC_SECRET) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
}
const payload = await getPayload({ config })
// Idempotency check
const existing = await payload.find({
// Check idempotency per form separately
const existingBirthday = await payload.find({
collection: 'forms',
where: { title: { in: ['Дні народження', 'Групові відвідування'] } },
limit: 2,
where: { title: { equals: 'Дні народження' } },
limit: 1,
overrideAccess: true,
})
if (existing.totalDocs >= 2) {
return NextResponse.json({ message: 'Forms already seeded', count: existing.totalDocs })
const existingGroup = await payload.find({
collection: 'forms',
where: { title: { equals: 'Групові відвідування' } },
limit: 1,
overrideAccess: true,
})
if (existingBirthday.totalDocs >= 1 && existingGroup.totalDocs >= 1) {
return NextResponse.json({ message: 'Forms already seeded', count: 2 })
}
// ── 1. Birthday form ──────────────────────────────────────────────
const birthdayForm = await payload.create({
collection: 'forms',
data: {
title: 'Дні народження',
fields: [
{
blockType: 'text',
name: 'name',
label: "Ваше ім'я",
required: true,
placeholder: 'Іван Іванов',
},
{
blockType: 'text',
name: 'phone',
label: 'Телефон',
required: true,
placeholder: '+38 (0__) ___-__-__',
},
{
blockType: 'email',
name: 'email',
label: 'Email',
placeholder: 'your@email.com',
},
{
blockType: 'number',
name: 'childAge',
label: 'Вік іменинника',
placeholder: '7',
},
{
blockType: 'number',
name: 'guestCount',
label: 'Кількість гостей',
placeholder: '15',
},
{
blockType: 'date',
name: 'preferredDate',
label: 'Бажана дата',
required: true,
width: 100,
},
{
blockType: 'textarea',
name: 'wishes',
label: 'Побажання',
placeholder: 'Тема свята, улюблені герої, особливі побажання...',
},
],
submitButtonLabel: 'Замовити святкування',
confirmationType: 'message',
} as never,
overrideAccess: true,
})
const birthdayForm =
existingBirthday.docs[0] ??
(await payload.create({
collection: 'forms',
data: {
title: 'Дні народження',
fields: [
{
blockType: 'text',
name: 'name',
label: "Ваше ім'я",
required: true,
placeholder: 'Іван Іванов',
},
{
blockType: 'text',
name: 'phone',
label: 'Телефон',
required: true,
placeholder: '+38 (0__) ___-__-__',
},
{
blockType: 'email',
name: 'email',
label: 'Email',
placeholder: 'your@email.com',
},
{
blockType: 'number',
name: 'childAge',
label: 'Вік іменинника',
placeholder: '7',
},
{
blockType: 'number',
name: 'guestCount',
label: 'Кількість гостей',
placeholder: '15',
},
{
blockType: 'date',
name: 'preferredDate',
label: 'Бажана дата',
required: true,
width: 100,
},
{
blockType: 'textarea',
name: 'wishes',
label: 'Побажання',
placeholder: 'Тема свята, улюблені герої, особливі побажання...',
},
],
submitButtonLabel: 'Замовити святкування',
confirmationType: 'message',
} as never,
overrideAccess: true,
}))
// ── 2. Group visits form ──────────────────────────────────────────
const groupForm = await payload.create({
collection: 'forms',
data: {
title: 'Групові відвідування',
fields: [
{
blockType: 'text',
name: 'name',
label: "Ваше ім'я",
required: true,
placeholder: 'Іван Іванов',
},
{
blockType: 'text',
name: 'phone',
label: 'Телефон',
required: true,
placeholder: '+38 (0__) ___-__-__',
},
{
blockType: 'email',
name: 'email',
label: 'Email',
placeholder: 'your@email.com',
},
{
blockType: 'number',
name: 'groupSize',
label: 'Кількість учасників',
required: true,
placeholder: '30',
},
{
blockType: 'date',
name: 'preferredDate',
label: 'Бажана дата',
width: 100,
},
{
blockType: 'select',
name: 'groupType',
label: 'Тип групи',
options: [
{ label: 'Шкільна екскурсія', value: 'school' },
{ label: 'Дитячий садок', value: 'kindergarten' },
{ label: 'Корпоратив', value: 'corporate' },
{ label: 'Інше', value: 'other' },
],
},
{
blockType: 'textarea',
name: 'message',
label: 'Повідомлення',
placeholder: 'Додаткові побажання або запитання...',
},
],
submitButtonLabel: 'Надіслати заявку',
confirmationType: 'message',
} as never,
overrideAccess: true,
})
const groupForm =
existingGroup.docs[0] ??
(await payload.create({
collection: 'forms',
data: {
title: 'Групові відвідування',
fields: [
{
blockType: 'text',
name: 'name',
label: "Ваше ім'я",
required: true,
placeholder: 'Іван Іванов',
},
{
blockType: 'text',
name: 'phone',
label: 'Телефон',
required: true,
placeholder: '+38 (0__) ___-__-__',
},
{
blockType: 'email',
name: 'email',
label: 'Email',
placeholder: 'your@email.com',
},
{
blockType: 'number',
name: 'groupSize',
label: 'Кількість учасників',
required: true,
placeholder: '30',
},
{
blockType: 'date',
name: 'preferredDate',
label: 'Бажана дата',
width: 100,
},
{
blockType: 'select',
name: 'groupType',
label: 'Тип групи',
options: [
{ label: 'Шкільна екскурсія', value: 'school' },
{ label: 'Дитячий садок', value: 'kindergarten' },
{ label: 'Корпоратив', value: 'corporate' },
{ label: 'Інше', value: 'other' },
],
},
{
blockType: 'textarea',
name: 'message',
label: 'Повідомлення',
placeholder: 'Додаткові побажання або запитання...',
},
],
submitButtonLabel: 'Надіслати заявку',
confirmationType: 'message',
} as never,
overrideAccess: true,
}))
// ── 3. Link forms to globals ──────────────────────────────────────
await payload.updateGlobal({