| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143 |
- /**
- * HTTP server for receiving Gogs webhooks
- */
- import http from 'http';
- import logger from './logger.js';
- import { WebhookHandler } from './webhookHandler.js';
- export class WebhookServer {
- constructor(options = {}) {
- this.host = options.host || '0.0.0.0';
- this.port = options.port || 3000;
- this.path = options.path || '/webhook';
- this.secret = options.secret || null;
- this.webhookHandler = new WebhookHandler();
- this.server = null;
- }
- /**
- * Parse the request body
- */
- _parseBody(req) {
- return new Promise((resolve, reject) => {
- let body = '';
- req.on('data', chunk => {
- body += chunk.toString();
- });
- req.on('end', () => {
- try {
- resolve(body ? JSON.parse(body) : {});
- } catch (error) {
- reject(new Error('Invalid JSON payload'));
- }
- });
- req.on('error', reject);
- });
- }
- /**
- * Verify webhook signature if secret is configured
- */
- _verifySignature(signature, payload) {
- if (!this.secret) {
- return true;
- }
- // Gogs uses HMAC-SHA256 for signatures
- // Format: X-Gogs-Signature: <hmac-sha256-hex>
- // Implementation can be added when secret verification is needed
- logger.info('Signature verification skipped (not implemented)');
- return true;
- }
- /**
- * Handle incoming HTTP requests
- */
- async _handleRequest(req, res) {
- const { method, url } = req;
- // Health check endpoint
- if (url === '/health' && method === 'GET') {
- res.writeHead(200, { 'Content-Type': 'application/json' });
- res.end(JSON.stringify({ status: 'ok', uptime: process.uptime() }));
- return;
- }
- // Webhook endpoint
- if (url === this.path && method === 'POST') {
- try {
- const payload = await this._parseBody(req);
- const eventType = req.headers['x-gogs-event'] || 'unknown';
- const signature = req.headers['x-gogs-signature'];
- const delivery = req.headers['x-gogs-delivery'];
- // Log basic request info
- logger.info('Received webhook', {
- event: eventType,
- delivery: delivery,
- hasSignature: !!signature
- });
- // Verify signature if configured
- if (!this._verifySignature(signature, payload)) {
- res.writeHead(401, { 'Content-Type': 'application/json' });
- res.end(JSON.stringify({ error: 'Invalid signature' }));
- return;
- }
- // Process webhook
- await this.webhookHandler.handle(eventType, payload, req.headers);
- // Send success response
- res.writeHead(200, { 'Content-Type': 'application/json' });
- res.end(JSON.stringify({ status: 'received' }));
- } catch (error) {
- logger.error('Error processing webhook', error);
- res.writeHead(400, { 'Content-Type': 'application/json' });
- res.end(JSON.stringify({ error: error.message }));
- }
- return;
- }
- // 404 for other routes
- res.writeHead(404, { 'Content-Type': 'application/json' });
- res.end(JSON.stringify({ error: 'Not found' }));
- }
- /**
- * Start the server
- */
- start() {
- this.server = http.createServer((req, res) => this._handleRequest(req, res));
- this.server.listen(this.port, this.host, () => {
- logger.info(`Webhook server listening on ${this.host}:${this.port}`);
- logger.info(`Webhook endpoint: http://${this.host}:${this.port}${this.path}`);
- logger.info(`Health check: http://${this.host}:${this.port}/health`);
- });
- return this;
- }
- /**
- * Stop the server
- */
- stop() {
- return new Promise((resolve) => {
- if (this.server) {
- this.server.close(() => {
- logger.info('Server stopped');
- resolve();
- });
- } else {
- resolve();
- }
- });
- }
- /**
- * Get the webhook handler instance for registering callbacks
- */
- getHandler() {
- return this.webhookHandler;
- }
- }
|