|
1 |
| -import { parse } from 'node:url'; |
2 | 1 | import { Connect, Identity, Inboxes, Messages, SpaceEvents, Utils } from '@graphprotocol/hypergraph';
|
3 | 2 | import { bytesToHex, randomBytes } from '@noble/hashes/utils.js';
|
4 | 3 | import cors from 'cors';
|
5 | 4 | import { Effect, Exit, Schema } from 'effect';
|
6 |
| -import express, { type Request, type Response } from 'express'; |
| 5 | +import express, { type NextFunction, type Request, type Response } from 'express'; |
| 6 | +import { parse } from 'node:url'; |
7 | 7 | import WebSocket, { WebSocketServer } from 'ws';
|
8 | 8 | import { addAppIdentityToSpaces } from './handlers/add-app-identity-to-spaces.js';
|
9 | 9 | import { applySpaceEvent } from './handlers/applySpaceEvent.js';
|
@@ -68,6 +68,28 @@ app.use(express.json({ limit: '2mb' }));
|
68 | 68 |
|
69 | 69 | app.use(cors());
|
70 | 70 |
|
| 71 | +// Request timeout middleware |
| 72 | +app.use((req: Request, res: Response, next: NextFunction) => { |
| 73 | + req.setTimeout(30000, () => { |
| 74 | + res.status(408).json({ error: 'Request timeout' }); |
| 75 | + }); |
| 76 | + next(); |
| 77 | +}); |
| 78 | + |
| 79 | +// Global error handling middleware |
| 80 | +app.use((error: Error, req: Request, res: Response, next: NextFunction) => { |
| 81 | + console.error('Unhandled error:', error); |
| 82 | + res.status(500).json({ |
| 83 | + error: 'Internal server error', |
| 84 | + message: process.env.NODE_ENV === 'development' ? error.message : 'Something went wrong', |
| 85 | + }); |
| 86 | +}); |
| 87 | + |
| 88 | +// 404 handler |
| 89 | +app.use('*', (req: Request, res: Response) => { |
| 90 | + res.status(404).json({ error: 'Route not found' }); |
| 91 | +}); |
| 92 | + |
71 | 93 | app.get('/', (_req, res) => {
|
72 | 94 | res.send('Server is running (v0.0.10)');
|
73 | 95 | });
|
@@ -557,6 +579,42 @@ const server = app.listen(PORT, () => {
|
557 | 579 | console.log(`Listening on port ${PORT}`);
|
558 | 580 | });
|
559 | 581 |
|
| 582 | +// Global process error handlers |
| 583 | +process.on('unhandledRejection', (reason, promise) => { |
| 584 | + console.error('Unhandled Rejection at:', promise, 'reason:', reason); |
| 585 | +}); |
| 586 | + |
| 587 | +process.on('uncaughtException', (error) => { |
| 588 | + console.error('Uncaught Exception:', error); |
| 589 | + // Graceful shutdown |
| 590 | + server.close(() => { |
| 591 | + console.log('Server closed due to uncaught exception'); |
| 592 | + process.exit(1); |
| 593 | + }); |
| 594 | +}); |
| 595 | + |
| 596 | +// Graceful shutdown handlers |
| 597 | +const gracefulShutdown = (signal: string) => { |
| 598 | + console.log(`Received ${signal}. Starting graceful shutdown...`); |
| 599 | + |
| 600 | + server.close(() => { |
| 601 | + console.log('HTTP server closed'); |
| 602 | + webSocketServer.close(() => { |
| 603 | + console.log('WebSocket server closed'); |
| 604 | + process.exit(0); |
| 605 | + }); |
| 606 | + }); |
| 607 | + |
| 608 | + // Force close after 30 seconds |
| 609 | + setTimeout(() => { |
| 610 | + console.error('Could not close connections in time, forcefully shutting down'); |
| 611 | + process.exit(1); |
| 612 | + }, 30000); |
| 613 | +}; |
| 614 | + |
| 615 | +process.on('SIGTERM', () => gracefulShutdown('SIGTERM')); |
| 616 | +process.on('SIGINT', () => gracefulShutdown('SIGINT')); |
| 617 | + |
560 | 618 | function broadcastSpaceEvents({
|
561 | 619 | spaceId,
|
562 | 620 | event,
|
|
0 commit comments