|
import { createServer } from 'http'; |
|
import { createHmac, timingSafeEqual } from 'crypto'; |
|
|
|
|
|
const GITHUB_WEBHOOK_SECRET = process.env.GITHUB_WEBHOOK_SECRET; |
|
const unparsedURL = process.env.GITHUB_WEBHOOK_URL || '/'; |
|
const GITHUB_WEBHOOK_URL = unparsedURL.startsWith('/') ? unparsedURL : `/${unparsedURL}`; |
|
const unparsedPort = process.env.SERVER_PORT || 3000; |
|
const SERVER_PORT = parseInt(unparsedPort, 10); |
|
|
|
if (!GITHUB_WEBHOOK_SECRET) { |
|
console.error('GITHUB_WEBHOOK_SECRET is required'); |
|
process.exit(1); |
|
} |
|
|
|
if (!SERVER_PORT || isNaN(SERVER_PORT)) { |
|
console.error('PORT is required'); |
|
process.exit(1); |
|
} |
|
|
|
/** |
|
* Verifies the signature of the incoming request |
|
* @param {IncomingMessage} req |
|
* @param {string} body |
|
* @returns {boolean} |
|
*/ |
|
function verifySignature(req, body) { |
|
const signature = req.headers['x-hub-signature-256']; |
|
if (!signature) return false; |
|
const hmac = createHmac('sha256', GITHUB_WEBHOOK_SECRET); |
|
hmac.update(body, 'utf-8'); |
|
const expectedSignature = `sha256=${hmac.digest('hex')}`; |
|
|
|
return timingSafeEqual( |
|
Buffer.from(signature), |
|
Buffer.from(expectedSignature) |
|
); |
|
} |
|
|
|
/** |
|
* Handles a GitHub webhook event |
|
* @param {string} event The event type |
|
* @param {object} payload The event payload |
|
*/ |
|
function handleWebhookEvent(event, payload) { |
|
console.log('Received GitHub Event:', event); |
|
console.log('Payload:', payload); |
|
} |
|
|
|
const server = createServer((req, res) => { |
|
if (req.method !== 'POST' || req.url !== GITHUB_WEBHOOK_URL) { |
|
res.statusCode = 404; |
|
res.end('Not Found'); |
|
return; |
|
} |
|
|
|
let body = ''; |
|
req.on('data', (chunk) => { |
|
body += chunk.toString(); |
|
}); |
|
|
|
req.on('end', () => { |
|
if (!verifySignature(req, body)) { |
|
res.statusCode = 403; |
|
res.end('Forbidden'); |
|
return; |
|
} |
|
|
|
const payload = JSON.parse(body); |
|
const event = req.headers['x-github-event']; |
|
res.statusCode = 200; |
|
res.end('OK'); |
|
|
|
handleWebhookEvent(event, payload); |
|
}); |
|
}); |
|
|
|
server.listen(SERVER_PORT, () => { |
|
console.log(`Server is listening on localhost:${SERVER_PORT}${GITHUB_WEBHOOK_URL}`); |
|
}); |