Auto-publish článkov na Next.js web
Návod krok po kroku, ako pripojiť svoj Next.js projekt (App Router aj Pages Router) k Optimalizácii pre AI. 1 súbor, 1 env premenná, 1 insert do DB — typicky 15-30 minút práce.
1. Predpoklady
- Plán PRO alebo AGENCY v Optimalizácii pre AI.
- Existujúci Next.js projekt na vlastnej doméne (verzia 13+, App alebo Pages Router).
- Možnosť deploy-núť zmenu (1 nový súbor + 1 env premenná).
- Miesto, kam články uložiť — typicky Postgres+Prisma, Supabase, alebo MDX súbory v repe.
2. Získať credentials v dashboarde
- Otvorte /dashboard/blog → banner „Nastavte publikovanie článkov" → Nastaviť.
- V kroku „Vyber platformu" klikni Webhook (auto-detect to často trafí — Next.js posiela hlavičku
x-powered-by: Next.js). - Vyplň formulár:
- Site URL:
https://mojfirma.sk/api/aio-webhook— presne táto cesta (vytvoríš ju v ďalšom kroku). - API Key (Bearer) — voliteľné, slúži ako prvá vrstva auth.
- Webhook Secret — klik „Vygenerovať" → skopíruj 64-znakový hex string. Tento si odlož — bude potrebný v kroku 4.
- Site URL:
- Klikni „Zobraziť kód pre môj framework" → tab Next.js (App Router) → Kopírovať.
Zatiaľ nestláčaj „Test connection" — endpoint na tvojom webe ešte neexistuje. Test spustíš až po kroku 5.
3. Vytvoriť webhook endpoint v Next.js
App RouterVytvor súbor app/api/aio-webhook/route.ts
app/api/aio-webhook/route.tsimport { NextRequest, NextResponse } from 'next/server'
import { createHmac, timingSafeEqual } from 'crypto'
export const runtime = 'nodejs'
const SECRET = process.env.AIO_WEBHOOK_SECRET!
export async function POST(req: NextRequest) {
const raw = await req.text()
const { event, data } = JSON.parse(raw)
// Test connection — wizard posiela {event:'test'} bez podpisu, povolíme prejsť
if (event === 'test') {
return NextResponse.json({ externalId: 'test', externalUrl: 'https://example.com' })
}
// Pre article.published vyžadujeme platný HMAC podpis
const sig = req.headers.get('x-webhook-signature') || ''
const expected = 'sha256=' + createHmac('sha256', SECRET).update(raw).digest('hex')
if (
sig.length !== expected.length ||
!timingSafeEqual(Buffer.from(sig), Buffer.from(expected))
) {
return NextResponse.json({ error: 'bad signature' }, { status: 401 })
}
// ⬇️ Tu uložte článok do svojej DB (Prisma / Supabase / iné)
// dostupné: data.title, data.htmlContent, data.excerpt, data.slug,
// data.coverImageUrl, data.seoTitle, data.metaDesc,
// data.tags, data.schemaMarkup
return NextResponse.json({
externalId: data.slug,
externalUrl: `https://mojfirma.sk/blog/${data.slug}`,
})
}Pages RouterAlternatívne: pages/api/aio-webhook.ts
pages/api/aio-webhook.tsimport type { NextApiRequest, NextApiResponse } from 'next'
import { createHmac, timingSafeEqual } from 'crypto'
export const config = { api: { bodyParser: false } }
const SECRET = process.env.AIO_WEBHOOK_SECRET!
async function readRaw(req: NextApiRequest): Promise<string> {
const chunks: Buffer[] = []
for await (const chunk of req) chunks.push(Buffer.from(chunk))
return Buffer.concat(chunks).toString('utf8')
}
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
if (req.method !== 'POST') return res.status(405).end()
const raw = await readRaw(req)
const { event, data } = JSON.parse(raw)
// Test connection — bez podpisu
if (event === 'test') return res.status(200).json({ externalId: 'test' })
// Pre article.published vyžadujeme platný HMAC podpis
const sig = (req.headers['x-webhook-signature'] as string) || ''
const expected = 'sha256=' + createHmac('sha256', SECRET).update(raw).digest('hex')
if (
sig.length !== expected.length ||
!timingSafeEqual(Buffer.from(sig), Buffer.from(expected))
) return res.status(401).json({ error: 'bad signature' })
// ⬇️ Tu uložte článok
res.status(200).json({
externalId: data.slug,
externalUrl: `https://mojfirma.sk/blog/${data.slug}`,
})
}HMAC verifikácia: Každý webhook od nás je podpísaný hlavičkou x-webhook-signature: sha256=... cez SHA-256 a váš secret. timingSafeEqual chráni pred timing attackmi.
4. Pridať env premennú
V lokálnom .env.local:
.env.localAIO_WEBHOOK_SECRET=<sem vlož secret z kroku 2>Na Verceli:
- Project Settings → Environment Variables → Add
- Name:
AIO_WEBHOOK_SECRET - Value: ten istý secret
- Scope: Production + Preview (oba)
Po pridaní env premennej Vercel nereštartuje existujúce nasadenie automaticky — buď redeploy alebo nový commit.
5. Uložiť článok do DB
V mieste označenom // ⬇️ Tu uložte článok doplňte insert podľa toho, čo používate. Tri najbežnejšie varianty:
Prisma + Postgres
await prisma.post.create({
data: {
title: data.title,
slug: data.slug,
htmlContent: data.htmlContent,
excerpt: data.excerpt,
coverImageUrl: data.coverImageUrl,
seoTitle: data.seoTitle,
metaDesc: data.metaDesc,
tags: data.tags,
publishedAt: new Date(),
},
})
// Optional: revalidate ISR cache
revalidatePath('/blog')
revalidatePath(`/blog/${data.slug}`)Supabase
const { error } = await supabase.from('posts').insert({
title: data.title,
slug: data.slug,
html_content: data.htmlContent,
excerpt: data.excerpt,
cover_image_url: data.coverImageUrl,
seo_title: data.seoTitle,
meta_desc: data.metaDesc,
tags: data.tags,
published_at: new Date(),
})
if (error) {
return NextResponse.json({ error: error.message }, { status: 500 })
}MDX súbory (statický blog)
import { writeFile } from 'fs/promises'
import path from 'path'
const frontmatter = `---
title: "${data.title}"
slug: "${data.slug}"
date: ${new Date().toISOString()}
cover: ${data.coverImageUrl}
excerpt: "${data.excerpt}"
---
`
await writeFile(
path.join(process.cwd(), 'content/blog', `${data.slug}.mdx`),
frontmatter + data.htmlContent
)
// + trigger Vercel deploy hook to rebuildPresnú schému payload-u (všetky polia, ktoré dostanete) nájdete v Webhook payload reference.
6. Deploy a test
git add app/api/aio-webhook/route.ts
git commit -m "Add AIO Tracking webhook endpoint"
git pushPo deploye sa vráťte do nášho dashboardu:
- Otvorte wizard znova (/dashboard/blog).
- Klik „Test connection" — my pošleme POST so
{ event: 'test' }. - Ak váš endpoint odpovie
200 OK→ ✅ zelená fajka. - Zapnite toggle „Auto-publish" → uložte.
7. Ako beží auto-flow
Od momentu, ako zapnete Auto-publish:
Content Hub vygeneruje a finalizuje článok
Platforma skontroluje, či máte Auto-publish ON a aktívnu Webhook connection
pg-boss queue dostane publish job
Worker zavolá publish() → POST na váš endpoint s HMAC podpisom
Váš endpoint overí podpis → uloží do DB → vráti { externalId, externalUrl }
V dashboarde uvidíte pri článku „Publikované na mojfirma.sk → otvoriť"
8. Checklist a riešenie problémov
Mini-checklist (pošlite vývojárovi)
- ☐ PRO/AGENCY plán aktívny
- ☐ V dashboarde: Webhook secret vygenerovaný a skopírovaný
- ☐ Súbor
app/api/aio-webhook/route.tsvytvorený - ☐
AIO_WEBHOOK_SECRETv .env.local + na Verceli - ☐ Insert do DB doplnený (Prisma / Supabase / iné)
- ☐ Deploy hotový (Vercel ukáže „Ready")
- ☐ Test connection v dashboarde = ✅
- ☐ Auto-publish toggle ON
Najčastejšie problémy
| Problém | Príčina | Fix |
|---|---|---|
| 401 bad signature | Iný secret v FE vs. v env | Skopírujte secret znova, redeploy |
| 404 | Endpoint na inej ceste | Site URL musí byť presne /api/aio-webhook |
| Test ✅, článok sa neuloží | TODO blok nedoplnený | Pridajte insert (viď krok 5) |
| DB má článok, web nie | Next.js ISR cache | Volajte revalidatePath() |
| SECRET is undefined | Env premenná nie je v Production scope | Vercel → Env Vars → preklikni scope |
Iný framework?
Vo wizarde priamo v dashboarde máte pripravené copy-paste snippety aj pre:
Princíp je rovnaký — POST endpoint, HMAC verifikácia, insert do DB.