- B1: Next.js 15 + Payload CMS 3.0 + Postgres 16, ESLint, Prettier, Husky, Vitest - B2: 9 collections, 6 globals, 12 Page Builder blocks, access control, slugify/revalidate hooks - B3: ezy.com.ua payments, Binotel HMAC webhook, leads API, Telegram bot, Resend email, rate limiting - B4: Tariffs collection with ezy API sync (cron + manual), dynamic pricing source-of-truth - B5: 13 test files covering unit libs and all API routes - B6: Dockerfile multi-stage, docker-compose.prod.yml, nginx.conf SSL, GitHub Actions CI/CD, health endpoint - B7: docs/admin-guide-ua.md (marketer guide), docs/deploy.md (VPS instructions), README quickstart Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
28 lines
942 B
TypeScript
28 lines
942 B
TypeScript
import { NextRequest, NextResponse } from 'next/server'
|
|
import { timingSafeEqual } from 'crypto'
|
|
import { syncTariffs } from '@/lib/syncTariffs'
|
|
import { logger } from '@/lib/logger'
|
|
|
|
const SECRET = process.env['SYNC_SECRET'] ?? ''
|
|
|
|
function safeCompare(a: string, b: string): boolean {
|
|
const aBuf = Buffer.from(a)
|
|
const bBuf = Buffer.from(b)
|
|
if (aBuf.length !== bBuf.length) return false
|
|
return timingSafeEqual(aBuf, bBuf)
|
|
}
|
|
|
|
export async function POST(req: NextRequest): Promise<NextResponse> {
|
|
const auth = req.headers.get('authorization') ?? ''
|
|
if (!SECRET || !safeCompare(auth, `Bearer ${SECRET}`)) {
|
|
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
|
|
}
|
|
|
|
try {
|
|
const result = await syncTariffs()
|
|
return NextResponse.json({ ok: true, ...result })
|
|
} catch (err) {
|
|
logger.error({ err }, 'Tariffs sync failed')
|
|
return NextResponse.json({ error: 'Sync failed' }, { status: 500 })
|
|
}
|
|
}
|