@0xobelisk/graphql-server
Version:
Tookit for interacting with dubhe graphql server
265 lines • 13.1 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.startServer = void 0;
const postgraphile_1 = require("postgraphile");
const pg_1 = require("pg");
const dotenv = __importStar(require("dotenv"));
const logger_1 = require("./utils/logger");
const plugins_1 = require("./plugins");
const enhanced_server_manager_1 = require("./plugins/enhanced-server-manager");
const subscription_config_1 = require("./config/subscription-config");
const universal_subscriptions_1 = require("./universal-subscriptions");
// Load environment variables
dotenv.config();
// Start server
const startServer = async (config) => {
// Set log level from config to environment (for logger compatibility)
process.env.LOG_LEVEL = config.debug ? 'debug' : 'info';
// Extract variables from config
const { port: PORT, databaseUrl: DATABASE_URL, schema: PG_SCHEMA, endpoint: GRAPHQL_ENDPOINT, cors: ENABLE_CORS, subscriptions: ENABLE_SUBSCRIPTIONS_BOOL, env: NODE_ENV } = config;
// Convert boolean values to string format
const ENABLE_SUBSCRIPTIONS = ENABLE_SUBSCRIPTIONS_BOOL ? 'true' : 'false';
// Build subscription configuration and refresh
logger_1.systemLogger.info('Refreshing subscription configuration with new settings...');
const subscriptionConfigInput = {
enableSubscriptions: ENABLE_SUBSCRIPTIONS_BOOL,
databaseUrl: DATABASE_URL,
port: PORT,
// Use configuration from ServerConfig instead of defaults
enableLiveQueries: config.enableLiveQueries,
enablePgSubscriptions: config.enablePgSubscriptions,
enableNativeWebSocket: config.enableNativeWebSocket,
realtimePort: config.realtimePort?.toString(),
maxConnections: config.maxConnections.toString(),
heartbeatInterval: config.heartbeatInterval.toString(),
debugNotifications: config.debugNotifications,
enableMetrics: config.enableMetrics
};
subscription_config_1.subscriptionConfig.refresh(subscriptionConfigInput);
// === Unified Connection Pool Architecture: Single pool handles all operations ===
logger_1.systemLogger.info('🔄 Connection pool configuration', {
maxConnections: config.maxConnections,
strategy: 'single-pool-unified',
operations: ['query', 'mutation', 'subscription']
});
// Unified connection pool - handles all GraphQL operations
const pgPool = new pg_1.Pool({
connectionString: DATABASE_URL,
// === Connection Pool Configuration ===
max: config.maxConnections, // Use configured maximum connections
min: Math.min(5, Math.floor(config.maxConnections * 0.1)), // Keep minimum connections
// === Balanced Configuration: Support both short-term queries and long-term subscriptions ===
connectionTimeoutMillis: 10000, // 10 second timeout (balanced value)
idleTimeoutMillis: 600000, // 10 minute idle cleanup (support subscriptions but not too long)
maxLifetimeSeconds: 3600, // 1 hour rotation (prevent connection leaks)
allowExitOnIdle: config.env === 'development'
});
// Add connection pool event listeners
pgPool.on('connect', (_client) => {
logger_1.dbLogger.debug('📤 New connection established', {
totalCount: pgPool.totalCount,
idleCount: pgPool.idleCount,
waitingCount: pgPool.waitingCount
});
});
pgPool.on('error', (err, _client) => {
logger_1.dbLogger.error('❌ Connection pool error', err, {
totalCount: pgPool.totalCount
});
});
const startTime = Date.now();
try {
// 1. Test database connection and scan table structure
logger_1.systemLogger.info('Initializing database connection and scanning table structure...', {
schema: PG_SCHEMA,
databaseUrl: DATABASE_URL.replace(/:[^:]*@/, ':****@') // Hide password
});
const introspector = new plugins_1.DatabaseIntrospector(pgPool, PG_SCHEMA);
const isConnected = await introspector.testConnection();
if (!isConnected) {
throw new Error('Database connection failed');
}
logger_1.dbLogger.info('Database connection successful', { schema: PG_SCHEMA });
const allTables = await introspector.getAllTables();
const tableNames = allTables.map((t) => t.table_name);
logger_1.dbLogger.info('Table structure scan completed', {
tableCount: allTables.length,
storeTableCount: tableNames.filter((name) => name.startsWith('store_')).length,
tableNames: tableNames.slice(0, 10) // Only show first 10 table names
});
// 2. Display subscription configuration status
const subscriptionConfigData = subscription_config_1.subscriptionConfig.getConfig();
logger_1.subscriptionLogger.info('📡 Subscription system configuration status', {
enableSubscriptions: subscriptionConfigData.enableSubscriptions,
capabilities: {
pgSubscriptions: subscriptionConfigData.capabilities.pgSubscriptions
},
recommendedMethod: 'pg-subscriptions',
walLevel: subscriptionConfigData.walLevel
});
// 3. Pre-generate store table information for dynamic queries
logger_1.subscriptionLogger.info('Pre-generating store table information for tool queries...');
const storeTablesInfo = await (0, universal_subscriptions_1.generateStoreTablesInfo)(pgPool);
const storeTableNames = Object.keys(storeTablesInfo);
logger_1.subscriptionLogger.info(`Discovered store tables: ${storeTableNames.join(', ')}`);
// 4. Create PostGraphile configuration
const postgraphileConfigOptions = {
port: PORT,
nodeEnv: NODE_ENV,
graphqlEndpoint: GRAPHQL_ENDPOINT,
enableSubscriptions: ENABLE_SUBSCRIPTIONS,
enableCors: ENABLE_CORS ? 'true' : 'false',
databaseUrl: DATABASE_URL,
availableTables: tableNames,
// Pass additional configuration from CLI
disableQueryLog: !config.debug, // Disable query log unless debug mode
enableQueryLog: config.debug, // Enable query log in debug mode
queryTimeout: config.queryTimeout
};
logger_1.serverLogger.info('Creating PostGraphile configuration', {
endpoint: GRAPHQL_ENDPOINT,
enableCors: ENABLE_CORS,
enableSubscriptions: ENABLE_SUBSCRIPTIONS,
debug: config.debug,
disableQueryLog: !config.debug,
enableQueryLog: config.debug
});
// Use simplified configuration
const postgraphileConfig = {
...(0, plugins_1.createPostGraphileConfig)(postgraphileConfigOptions),
...subscription_config_1.subscriptionConfig.generatePostGraphileConfig()
};
// Add tools query plugin
const toolsPlugin = (0, universal_subscriptions_1.createUniversalSubscriptionsPlugin)(storeTablesInfo);
postgraphileConfig.appendPlugins = [...(postgraphileConfig.appendPlugins || []), toolsPlugin];
// 5. Create PostGraphile middleware
console.log('🔧 Creating PostGraphile middleware...');
const postgraphileMiddleware = (0, postgraphile_1.postgraphile)(pgPool, PG_SCHEMA, {
...postgraphileConfig
});
console.log('✅ PostGraphile middleware creation completed:', typeof postgraphileMiddleware);
// 6. Configure welcome page
const welcomeConfig = {
port: PORT,
graphqlEndpoint: GRAPHQL_ENDPOINT,
nodeEnv: NODE_ENV,
schema: PG_SCHEMA,
enableCors: ENABLE_CORS ? 'true' : 'false',
enableSubscriptions: ENABLE_SUBSCRIPTIONS
};
// 7. Create Express server manager
const serverManager = new enhanced_server_manager_1.EnhancedServerManager();
// 8. Create Express server
await serverManager.createEnhancedServer({
postgraphileMiddleware,
pgPool,
tableNames,
databaseUrl: DATABASE_URL,
allTables,
welcomeConfig,
postgraphileConfigOptions
});
// 9. Start Express server
await serverManager.startServer();
(0, logger_1.logPerformance)('Express server startup', startTime, {
port: PORT,
tableCount: allTables.length,
storeTableCount: storeTableNames.length,
nodeEnv: NODE_ENV,
framework: 'Express',
capabilities: {
pgSubscriptions: subscriptionConfigData.capabilities.pgSubscriptions
}
});
// 10. Display usage instructions
if (NODE_ENV === 'development') {
console.log('\n' + '='.repeat(80));
console.log('📖 Quick Access (Express Architecture):');
console.log(`Visit http://localhost:${PORT}/ to view homepage`);
console.log(`Visit http://localhost:${PORT}/playground to use GraphQL Playground`);
console.log(`Visit http://localhost:${PORT}/health to check server status`);
console.log(`Visit http://localhost:${PORT}/subscription-config to get client configuration`);
console.log(`Visit http://localhost:${PORT}/subscription-docs to view configuration guide`);
console.log('='.repeat(80) + '\n');
}
// 11. Set up simple and direct shutdown handling
let isShuttingDown = false;
const quickShutdown = (signal) => {
if (isShuttingDown) {
logger_1.systemLogger.info('⚡ Force exiting process...');
process.exit(0);
}
isShuttingDown = true;
logger_1.systemLogger.info(`🛑 Received ${signal} signal, shutting down Express server...`);
// Set 1 second force exit timeout
setTimeout(() => {
logger_1.systemLogger.info('⚡ Quick exit');
process.exit(0);
}, 1000);
// Try to shutdown Express server quickly
serverManager.quickShutdown().finally(() => {
process.exit(0);
});
};
process.on('SIGINT', () => quickShutdown('SIGINT'));
process.on('SIGTERM', () => quickShutdown('SIGTERM'));
// Simplified exception handling
process.on('unhandledRejection', (reason) => {
console.error('❌ Unhandled Promise rejection:', reason);
});
process.on('uncaughtException', (error) => {
console.error('❌ Uncaught exception:', error.message);
process.exit(1);
});
}
catch (error) {
logger_1.systemLogger.error('Failed to start Express server', error, {
databaseUrl: DATABASE_URL.replace(/:[^:]*@/, ':****@'),
schema: PG_SCHEMA,
port: PORT
});
logger_1.systemLogger.info('💡 Possible causes:');
logger_1.systemLogger.info('1. Database connection failed - check DATABASE_URL');
logger_1.systemLogger.info('2. Expected table structure not found in database - ensure dubhe-indexer is running');
logger_1.systemLogger.info('3. Permission issues - ensure database user has sufficient permissions');
logger_1.systemLogger.info('4. Missing dependencies - run pnpm install');
// Display subscription configuration help
console.log('\n' + subscription_config_1.subscriptionConfig.generateDocumentation());
process.exit(1);
}
};
exports.startServer = startServer;
//# sourceMappingURL=server.js.map