import { NextRequest } from "next/server" import crypto from "crypto" import { createPost, getPostByGid, updatePost } from "@/lib/db" import { cleanTitle, slugify } from "@/lib/slug" import { cacheCover } from "@/lib/cover-cache" function verifySignature(bodyText: string, signature: string, secret: string): boolean { try { const hmac = crypto.createHmac("sha256", secret) hmac.update(bodyText) const expected = hmac.digest("hex") return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(signature)) } catch { return false } } export async function POST(req: NextRequest) { try { const rawBody = await req.text() const secret = process.env.WEBHOOK_SECRET || process.env.FANSUB_WEBHOOK_SECRET || "" if (secret) { const signature = req.headers.get("x-signature-256") || req.headers.get("X-Signature-256") || "" if (!signature || !verifySignature(rawBody, signature, secret)) { return Response.json({ error: "Unauthorized: invalid signature" }, { status: 401 }) } } const payload = JSON.parse(rawBody) const gid = payload.gid const rawTitle = payload.title if (!gid || !rawTitle) { return Response.json({ error: "Invalid payload: gid and title required" }, { status: 400 }) } const title = cleanTitle(rawTitle) const tags = Array.isArray(payload.tags) ? payload.tags : [] const numPages = payload.num_pages || payload.pages || 0 const synopsis = payload.synopsis || "" const sourceUrl = payload.url || (payload.source === "nhentai" ? `https://nhentai.net/g/${gid}/` : (payload.source === "ehentai" ? `https://e-hentai.org/g/${gid}/` : undefined)) const slug = slugify(title, gid) let post = getPostByGid(gid) if (!post) { post = createPost({ gid, title, title_jpn: payload.title_jpn || undefined, artist: payload.artist || undefined, parody: payload.parody || undefined, tags, num_pages: numPages, source: payload.source || undefined, cover_url: payload.cover_url || undefined, url: sourceUrl, summary: synopsis || undefined, slug, published: 1, }) } else { post = updatePost(post.id, { title, title_jpn: payload.title_jpn || post.title_jpn || undefined, artist: payload.artist || post.artist || undefined, parody: payload.parody || post.parody || undefined, tags: tags.length > 0 ? tags : post.tags, num_pages: numPages || post.num_pages, source: payload.source || post.source || undefined, cover_url: payload.cover_url || post.cover_url || undefined, url: sourceUrl || post.url || undefined, summary: synopsis || post.summary || undefined, })! } await cacheCover(gid) return Response.json({ success: true, gid, post, }) } catch (err) { return Response.json({ error: String(err) }, { status: 500 }) } }