@0xobelisk/graphql-server
Version:
Tookit for interacting with dubhe graphql server
262 lines • 11.1 kB
JavaScript
;
// Express server manager - using Express framework and PostgreSQL subscriptions
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.EnhancedServerManager = void 0;
const express_1 = __importDefault(require("express"));
const http_1 = require("http");
const cors_1 = __importDefault(require("cors"));
const postgraphile_1 = require("postgraphile");
const subscription_config_1 = require("../config/subscription-config");
const logger_1 = require("../utils/logger");
const welcome_page_1 = require("./welcome-page");
const postgraphile_config_1 = require("./postgraphile-config");
class EnhancedServerManager {
config;
app = null;
httpServer = null;
pgPool = null;
constructor() {
this.config = subscription_config_1.subscriptionConfig.getConfig();
}
// Create Express application
createExpressApp(serverConfig) {
const { postgraphileMiddleware, allTables, welcomeConfig, postgraphileConfigOptions } = serverConfig;
const app = (0, express_1.default)();
// Middleware configuration
app.use((0, cors_1.default)({
origin: '*',
methods: ['GET', 'POST', 'OPTIONS'],
allowedHeaders: ['Content-Type', 'Authorization']
}));
// Request logging middleware
app.use((req, res, next) => {
const startTime = Date.now();
res.on('finish', () => {
(0, logger_1.logExpress)(req.method, req.path, res.statusCode, startTime, {
userAgent: req.get('user-agent')?.substring(0, 50)
});
});
next();
});
// Route configuration
// Root path - welcome page
app.get('/', (req, res) => {
res.set('Content-Type', 'text/html; charset=utf-8');
res.send((0, welcome_page_1.createWelcomePage)(allTables, welcomeConfig));
});
// GraphQL Playground
app.get('/playground', (req, res) => {
res.set('Content-Type', 'text/html; charset=utf-8');
res.send((0, postgraphile_config_1.createPlaygroundHtml)(postgraphileConfigOptions));
});
// Redirect old GraphiQL paths
app.get('/graphiql*', (req, res) => {
logger_1.serverLogger.info('Redirecting old GraphiQL path', {
from: req.path,
to: '/playground'
});
res.redirect(301, '/playground');
});
// Health check endpoint
app.get('/health', (req, res) => {
res.json({
status: 'healthy',
subscriptions: this.getSubscriptionStatus(),
timestamp: new Date().toISOString()
});
});
// Subscription configuration endpoint
app.get('/subscription-config', (req, res) => {
res.json(subscription_config_1.subscriptionConfig.generateClientConfig());
});
// Configuration documentation endpoint
app.get('/subscription-docs', (req, res) => {
res.set('Content-Type', 'text/plain');
res.send(subscription_config_1.subscriptionConfig.generateDocumentation());
});
// Add connection pool status endpoint
app.get('/pool-status', (req, res) => {
if (this.pgPool) {
const poolStatus = {
totalCount: this.pgPool.totalCount,
idleCount: this.pgPool.idleCount,
waitingCount: this.pgPool.waitingCount,
maxConnections: this.pgPool.options.max || 'Not set',
minConnections: this.pgPool.options.min || 'Not set'
};
res.json({
status: 'ok',
connectionPool: poolStatus,
strategy: 'single-pool-unified',
operations: ['query', 'mutation', 'subscription'],
timestamp: new Date().toISOString(),
uptime: process.uptime(),
memory: process.memoryUsage()
});
}
else {
res.status(503).json({
status: 'error',
message: 'Connection pool not available'
});
}
});
// PostGraphile middleware - mount at root path, let PostGraphile handle routing itself
app.use((req, res, next) => {
// Check if PostGraphile middleware exists
if (!postgraphileMiddleware) {
console.error('❌ PostGraphile middleware is null!');
if (req.path.startsWith('/graphql')) {
res.status(500).json({
error: 'PostGraphile middleware not properly initialized'
});
return;
}
next();
return;
}
try {
postgraphileMiddleware(req, res, next);
}
catch (error) {
console.error('❌ PostGraphile middleware execution error:', error);
if (req.path.startsWith('/graphql')) {
res.status(500).json({
error: 'PostGraphile execution error',
details: error instanceof Error ? error.message : String(error)
});
return;
}
next();
}
});
// Error handling middleware
app.use((err, req, res, _next) => {
logger_1.serverLogger.error('Express error handling', err, {
url: req.originalUrl,
method: req.method,
userAgent: req.get('user-agent')?.substring(0, 50)
});
res.status(500).send('Internal Server Error');
});
return app;
}
// Create and configure HTTP server
async createEnhancedServer(serverConfig) {
const { postgraphileMiddleware, pgPool } = serverConfig;
// Store pool references for monitoring
this.pgPool = pgPool;
// Create Express application
this.app = this.createExpressApp(serverConfig);
// Create HTTP server
this.httpServer = (0, http_1.createServer)(this.app);
// Enable PostgreSQL subscriptions and WebSocket support
if (this.config.capabilities.pgSubscriptions) {
(0, postgraphile_1.enhanceHttpServerWithSubscriptions)(this.httpServer, postgraphileMiddleware, {
// Enable WebSocket transport
graphqlRoute: '/graphql'
});
logger_1.systemLogger.info('✅ PostgreSQL subscriptions and WebSocket enabled', {
pgSubscriptions: this.config.capabilities.pgSubscriptions,
webSocket: true
});
}
logger_1.serverLogger.info('🚀 Express server creation completed', {
framework: 'Express',
graphqlPort: this.config.graphqlPort,
capabilities: {
pgSubscriptions: this.config.capabilities.pgSubscriptions
},
recommendedMethod: 'pg-subscriptions'
});
return this.httpServer;
}
// Start server
async startServer() {
if (!this.httpServer) {
throw new Error('Server not created, please call createEnhancedServer() first');
}
return new Promise((resolve, reject) => {
this.httpServer.listen(this.config.graphqlPort, (err) => {
if (err) {
reject(err);
return;
}
this.logServerStatus();
resolve();
});
});
}
// Log server status
logServerStatus() {
const clientConfig = subscription_config_1.subscriptionConfig.generateClientConfig();
logger_1.serverLogger.info('🎉 Express GraphQL server started successfully!', {
port: this.config.graphqlPort,
framework: 'Express',
endpoints: {
home: `http://localhost:${this.config.graphqlPort}/`,
playground: `http://localhost:${this.config.graphqlPort}/playground`,
graphql: clientConfig.graphqlEndpoint,
subscription: clientConfig.subscriptionEndpoint,
health: `http://localhost:${this.config.graphqlPort}/health`,
config: `http://localhost:${this.config.graphqlPort}/subscription-config`,
docs: `http://localhost:${this.config.graphqlPort}/subscription-docs`
}
});
// Display main access links
console.log('\n' + '🌟'.repeat(30));
console.log('🏠 Homepage: ' + `http://localhost:${this.config.graphqlPort}/`);
console.log('🎮 Playground: ' + `http://localhost:${this.config.graphqlPort}/playground`);
console.log('🔗 GraphQL: ' + clientConfig.graphqlEndpoint);
console.log('📡 WebSocket: ' + clientConfig.subscriptionEndpoint);
console.log('🌟'.repeat(30) + '\n');
}
// Get subscription status
getSubscriptionStatus() {
return {
enabled: this.config.capabilities.pgSubscriptions,
method: 'pg-subscriptions',
config: subscription_config_1.subscriptionConfig.generateClientConfig()
};
}
// Quick shutdown
async quickShutdown() {
logger_1.systemLogger.info('🛑 Starting quick shutdown of Express server...');
if (this.httpServer) {
this.httpServer.close();
logger_1.systemLogger.info('✅ HTTP server closed');
}
logger_1.systemLogger.info('🎯 Express server quick shutdown completed');
}
// Graceful shutdown
async gracefulShutdown(pgPool) {
logger_1.systemLogger.info('🛑 Starting graceful shutdown of Express server...');
const shutdownPromises = [];
// Close HTTP server
if (this.httpServer) {
shutdownPromises.push(new Promise((resolve) => {
this.httpServer.close(() => {
logger_1.systemLogger.info('✅ HTTP server closed');
resolve();
});
}));
}
// Close database connection pool
shutdownPromises.push(pgPool.end().then(() => {
logger_1.systemLogger.info('✅ Database connection pool closed');
}));
try {
await Promise.all(shutdownPromises);
logger_1.systemLogger.info('🎯 Express server graceful shutdown completed');
}
catch (error) {
logger_1.systemLogger.error('❌ Error occurred during shutdown process', error);
throw error;
}
}
}
exports.EnhancedServerManager = EnhancedServerManager;
//# sourceMappingURL=enhanced-server-manager.js.map