server.js 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  1. /**
  2. * HTTP server for receiving Gogs webhooks
  3. */
  4. import http from 'http';
  5. import logger from './logger.js';
  6. import { WebhookHandler } from './webhookHandler.js';
  7. export class WebhookServer {
  8. constructor(options = {}) {
  9. this.host = options.host || '0.0.0.0';
  10. this.port = options.port || 3000;
  11. this.path = options.path || '/webhook';
  12. this.secret = options.secret || null;
  13. this.webhookHandler = new WebhookHandler();
  14. this.server = null;
  15. }
  16. /**
  17. * Parse the request body
  18. */
  19. _parseBody(req) {
  20. return new Promise((resolve, reject) => {
  21. let body = '';
  22. req.on('data', chunk => {
  23. body += chunk.toString();
  24. });
  25. req.on('end', () => {
  26. try {
  27. resolve(body ? JSON.parse(body) : {});
  28. } catch (error) {
  29. reject(new Error('Invalid JSON payload'));
  30. }
  31. });
  32. req.on('error', reject);
  33. });
  34. }
  35. /**
  36. * Verify webhook signature if secret is configured
  37. */
  38. _verifySignature(signature, payload) {
  39. if (!this.secret) {
  40. return true;
  41. }
  42. // Gogs uses HMAC-SHA256 for signatures
  43. // Format: X-Gogs-Signature: <hmac-sha256-hex>
  44. // Implementation can be added when secret verification is needed
  45. logger.info('Signature verification skipped (not implemented)');
  46. return true;
  47. }
  48. /**
  49. * Handle incoming HTTP requests
  50. */
  51. async _handleRequest(req, res) {
  52. const { method, url } = req;
  53. // Health check endpoint
  54. if (url === '/health' && method === 'GET') {
  55. res.writeHead(200, { 'Content-Type': 'application/json' });
  56. res.end(JSON.stringify({ status: 'ok', uptime: process.uptime() }));
  57. return;
  58. }
  59. // Webhook endpoint
  60. if (url === this.path && method === 'POST') {
  61. try {
  62. const payload = await this._parseBody(req);
  63. const eventType = req.headers['x-gogs-event'] || 'unknown';
  64. const signature = req.headers['x-gogs-signature'];
  65. const delivery = req.headers['x-gogs-delivery'];
  66. // Log basic request info
  67. logger.info('Received webhook', {
  68. event: eventType,
  69. delivery: delivery,
  70. hasSignature: !!signature
  71. });
  72. // Verify signature if configured
  73. if (!this._verifySignature(signature, payload)) {
  74. res.writeHead(401, { 'Content-Type': 'application/json' });
  75. res.end(JSON.stringify({ error: 'Invalid signature' }));
  76. return;
  77. }
  78. // Process webhook
  79. await this.webhookHandler.handle(eventType, payload, req.headers);
  80. // Send success response
  81. res.writeHead(200, { 'Content-Type': 'application/json' });
  82. res.end(JSON.stringify({ status: 'received' }));
  83. } catch (error) {
  84. logger.error('Error processing webhook', error);
  85. res.writeHead(400, { 'Content-Type': 'application/json' });
  86. res.end(JSON.stringify({ error: error.message }));
  87. }
  88. return;
  89. }
  90. // 404 for other routes
  91. res.writeHead(404, { 'Content-Type': 'application/json' });
  92. res.end(JSON.stringify({ error: 'Not found' }));
  93. }
  94. /**
  95. * Start the server
  96. */
  97. start() {
  98. this.server = http.createServer((req, res) => this._handleRequest(req, res));
  99. this.server.listen(this.port, this.host, () => {
  100. logger.info(`Webhook server listening on ${this.host}:${this.port}`);
  101. logger.info(`Webhook endpoint: http://${this.host}:${this.port}${this.path}`);
  102. logger.info(`Health check: http://${this.host}:${this.port}/health`);
  103. });
  104. return this;
  105. }
  106. /**
  107. * Stop the server
  108. */
  109. stop() {
  110. return new Promise((resolve) => {
  111. if (this.server) {
  112. this.server.close(() => {
  113. logger.info('Server stopped');
  114. resolve();
  115. });
  116. } else {
  117. resolve();
  118. }
  119. });
  120. }
  121. /**
  122. * Get the webhook handler instance for registering callbacks
  123. */
  124. getHandler() {
  125. return this.webhookHandler;
  126. }
  127. }