mirror of
https://github.com/ershisan99/www.git
synced 2025-12-17 12:34:17 +00:00
add webhook handler
This commit is contained in:
109
src/app/api/neatqueue-webhook/route.ts
Normal file
109
src/app/api/neatqueue-webhook/route.ts
Normal file
@@ -0,0 +1,109 @@
|
||||
import crypto from 'node:crypto'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
|
||||
const EXPECTED_QUERY_SECRET = process.env.WEBHOOK_QUERY_SECRET
|
||||
const QUERY_PARAM_NAME = 'token'
|
||||
|
||||
/**
|
||||
* Verifies the secret from the query parameter.
|
||||
*/
|
||||
function verifyQuerySecret(req: NextRequest): boolean {
|
||||
if (!EXPECTED_QUERY_SECRET) {
|
||||
console.error(
|
||||
'Webhook query secret is not configured in environment variables.'
|
||||
)
|
||||
return false
|
||||
}
|
||||
|
||||
const providedSecret = req.nextUrl.searchParams.get(QUERY_PARAM_NAME)
|
||||
|
||||
if (!providedSecret) {
|
||||
console.warn(`Query parameter "${QUERY_PARAM_NAME}" missing.`)
|
||||
return false
|
||||
}
|
||||
|
||||
const expectedBuffer = Buffer.from(EXPECTED_QUERY_SECRET, 'utf8')
|
||||
const providedBuffer = Buffer.from(providedSecret, 'utf8')
|
||||
|
||||
if (
|
||||
expectedBuffer.length !== providedBuffer.length ||
|
||||
!crypto.timingSafeEqual(expectedBuffer, providedBuffer)
|
||||
) {
|
||||
console.warn('Invalid query secret provided.')
|
||||
return false
|
||||
}
|
||||
|
||||
console.log('Query secret verified successfully.')
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles POST requests to the /api/webhooks endpoint.
|
||||
* Verifies query secret, logs payload, and handles actions.
|
||||
*/
|
||||
export async function POST(req: NextRequest) {
|
||||
let payload: any
|
||||
|
||||
try {
|
||||
const isVerified = verifyQuerySecret(req)
|
||||
if (!isVerified) {
|
||||
console.log('Webhook verification failed (query secret).')
|
||||
return NextResponse.json(
|
||||
{ message: 'Unauthorized: Invalid or missing secret' },
|
||||
{ status: 401 }
|
||||
)
|
||||
}
|
||||
|
||||
payload = await req.json()
|
||||
|
||||
console.log(
|
||||
'--- Verified Webhook Received (Query Auth) ---',
|
||||
new Date().toISOString(),
|
||||
'---\n',
|
||||
JSON.stringify(payload, null, 2),
|
||||
'\n--- End Webhook ---'
|
||||
)
|
||||
|
||||
console.log(
|
||||
`Action: ${payload?.action || 'Unknown'}. Sending generic success response.`
|
||||
)
|
||||
return NextResponse.json(
|
||||
{ message: 'Webhook received successfully' },
|
||||
{ status: 200 }
|
||||
)
|
||||
} catch (error: any) {
|
||||
console.error('!!! Error processing webhook:', error)
|
||||
try {
|
||||
// Attempt to read body on error
|
||||
const errorBody = await req.clone().text()
|
||||
console.error('Raw request body on error:', errorBody)
|
||||
} catch (bodyError) {
|
||||
console.error('Could not read raw request body on error:', bodyError)
|
||||
}
|
||||
|
||||
if (error instanceof SyntaxError) {
|
||||
return NextResponse.json(
|
||||
{ message: 'Invalid JSON payload' },
|
||||
{ status: 400 }
|
||||
)
|
||||
}
|
||||
if (error.message.includes('Webhook query secret is not configured')) {
|
||||
return NextResponse.json(
|
||||
{ message: 'Internal Server Error: Webhook secret not configured' },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
|
||||
return NextResponse.json(
|
||||
{ message: 'Internal Server Error processing webhook' },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export async function GET(req: NextRequest) {
|
||||
return NextResponse.json(
|
||||
{ message: 'Method Not Allowed. Please use POST.' },
|
||||
{ status: 405 }
|
||||
)
|
||||
}
|
||||
@@ -17,6 +17,7 @@ export const env = createEnv({
|
||||
DISCORD_BOT_TOKEN: z.string(),
|
||||
DATABASE_URL: z.string().url(),
|
||||
REDIS_URL: z.string().url(),
|
||||
WEBHOOK_QUERY_SECRET: z.string(),
|
||||
NODE_ENV: z
|
||||
.enum(['development', 'test', 'production'])
|
||||
.default('development'),
|
||||
@@ -44,6 +45,7 @@ export const env = createEnv({
|
||||
REDIS_URL: process.env.REDIS_URL,
|
||||
NODE_ENV: process.env.NODE_ENV,
|
||||
CRON_SECRET: process.env.CRON_SECRET,
|
||||
WEBHOOK_QUERY_SECRET: process.env.WEBHOOK_QUERY_SECRET,
|
||||
},
|
||||
/**
|
||||
* Run `build` or `dev` with `SKIP_ENV_VALIDATION` to skip env validation. This is especially
|
||||
|
||||
Reference in New Issue
Block a user