@0xobelisk/graphql-server 1.2.0-pre.24

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. package/Dockerfile +31 -0
  2. package/EXPRESS_MIGRATION.md +176 -0
  3. package/LICENSE +92 -0
  4. package/README.md +908 -0
  5. package/dist/config/subscription-config.d.ts +47 -0
  6. package/dist/config/subscription-config.d.ts.map +1 -0
  7. package/dist/config/subscription-config.js +133 -0
  8. package/dist/config/subscription-config.js.map +1 -0
  9. package/dist/index.d.ts +2 -0
  10. package/dist/index.d.ts.map +1 -0
  11. package/dist/index.js +217 -0
  12. package/dist/index.js.map +1 -0
  13. package/dist/plugins/all-fields-filter-plugin.d.ts +4 -0
  14. package/dist/plugins/all-fields-filter-plugin.d.ts.map +1 -0
  15. package/dist/plugins/all-fields-filter-plugin.js +132 -0
  16. package/dist/plugins/all-fields-filter-plugin.js.map +1 -0
  17. package/dist/plugins/database-introspector.d.ts +23 -0
  18. package/dist/plugins/database-introspector.d.ts.map +1 -0
  19. package/dist/plugins/database-introspector.js +96 -0
  20. package/dist/plugins/database-introspector.js.map +1 -0
  21. package/dist/plugins/enhanced-playground.d.ts +9 -0
  22. package/dist/plugins/enhanced-playground.d.ts.map +1 -0
  23. package/dist/plugins/enhanced-playground.js +97 -0
  24. package/dist/plugins/enhanced-playground.js.map +1 -0
  25. package/dist/plugins/enhanced-server-manager.d.ts +28 -0
  26. package/dist/plugins/enhanced-server-manager.d.ts.map +1 -0
  27. package/dist/plugins/enhanced-server-manager.js +232 -0
  28. package/dist/plugins/enhanced-server-manager.js.map +1 -0
  29. package/dist/plugins/index.d.ts +9 -0
  30. package/dist/plugins/index.d.ts.map +1 -0
  31. package/dist/plugins/index.js +26 -0
  32. package/dist/plugins/index.js.map +1 -0
  33. package/dist/plugins/postgraphile-config.d.ts +94 -0
  34. package/dist/plugins/postgraphile-config.d.ts.map +1 -0
  35. package/dist/plugins/postgraphile-config.js +183 -0
  36. package/dist/plugins/postgraphile-config.js.map +1 -0
  37. package/dist/plugins/query-filter.d.ts +4 -0
  38. package/dist/plugins/query-filter.d.ts.map +1 -0
  39. package/dist/plugins/query-filter.js +42 -0
  40. package/dist/plugins/query-filter.js.map +1 -0
  41. package/dist/plugins/simple-naming.d.ts +4 -0
  42. package/dist/plugins/simple-naming.d.ts.map +1 -0
  43. package/dist/plugins/simple-naming.js +79 -0
  44. package/dist/plugins/simple-naming.js.map +1 -0
  45. package/dist/plugins/welcome-page.d.ts +11 -0
  46. package/dist/plugins/welcome-page.d.ts.map +1 -0
  47. package/dist/plugins/welcome-page.js +203 -0
  48. package/dist/plugins/welcome-page.js.map +1 -0
  49. package/dist/universal-subscriptions.d.ts +32 -0
  50. package/dist/universal-subscriptions.d.ts.map +1 -0
  51. package/dist/universal-subscriptions.js +318 -0
  52. package/dist/universal-subscriptions.js.map +1 -0
  53. package/dist/utils/logger/index.d.ts +80 -0
  54. package/dist/utils/logger/index.d.ts.map +1 -0
  55. package/dist/utils/logger/index.js +232 -0
  56. package/dist/utils/logger/index.js.map +1 -0
  57. package/docker-compose.yml +87 -0
  58. package/package.json +71 -0
  59. package/server.log +62 -0
  60. package/src/config/subscription-config.ts +186 -0
  61. package/src/index.ts +239 -0
  62. package/src/plugins/README.md +123 -0
  63. package/src/plugins/all-fields-filter-plugin.ts +158 -0
  64. package/src/plugins/database-introspector.ts +126 -0
  65. package/src/plugins/enhanced-playground.ts +105 -0
  66. package/src/plugins/enhanced-server-manager.ts +282 -0
  67. package/src/plugins/index.ts +9 -0
  68. package/src/plugins/postgraphile-config.ts +226 -0
  69. package/src/plugins/query-filter.ts +50 -0
  70. package/src/plugins/simple-naming.ts +105 -0
  71. package/src/plugins/welcome-page.ts +218 -0
  72. package/src/universal-subscriptions.ts +397 -0
  73. package/src/utils/logger/README.md +193 -0
  74. package/src/utils/logger/index.ts +315 -0
  75. package/sui-indexer-schema.graphql +1004 -0
  76. package/test-express.js +124 -0
  77. package/test_listen_subscription.js +121 -0
  78. package/test_notification.js +63 -0
  79. package/tsconfig.json +28 -0
@@ -0,0 +1,87 @@
1
+ version: "3.8"
2
+
3
+ services:
4
+ postgres:
5
+ image: postgres:15-alpine
6
+ environment:
7
+ POSTGRES_DB: dubhe_graphql
8
+ POSTGRES_USER: postgres
9
+ POSTGRES_PASSWORD: postgres
10
+ ports:
11
+ - "5432:5432"
12
+ volumes:
13
+ - postgres_data:/var/lib/postgresql/data
14
+ - ./init.sql:/docker-entrypoint-initdb.d/init.sql
15
+ healthcheck:
16
+ test: ["CMD-SHELL", "pg_isready -U postgres"]
17
+ interval: 5s
18
+ timeout: 5s
19
+ retries: 5
20
+
21
+ redis:
22
+ image: redis:7-alpine
23
+ ports:
24
+ - "6379:6379"
25
+ command: redis-server --appendonly yes
26
+ volumes:
27
+ - redis_data:/data
28
+ healthcheck:
29
+ test: ["CMD", "redis-cli", "ping"]
30
+ interval: 5s
31
+ timeout: 3s
32
+ retries: 5
33
+
34
+ graphql-server:
35
+ build:
36
+ context: .
37
+ dockerfile: Dockerfile
38
+ ports:
39
+ - "4000:4000"
40
+ environment:
41
+ DATABASE_URL: postgres://postgres:postgres@postgres:5432/dubhe_graphql
42
+ NODE_ENV: production
43
+ PORT: 4000
44
+ GRAPHQL_ENDPOINT: /graphql
45
+ PG_SCHEMA: public
46
+ ENABLE_CORS: "true"
47
+ ENABLE_SUBSCRIPTIONS: "true"
48
+ WATCH_PG: "false"
49
+ depends_on:
50
+ postgres:
51
+ condition: service_healthy
52
+ redis:
53
+ condition: service_healthy
54
+ restart: unless-stopped
55
+ healthcheck:
56
+ test:
57
+ [
58
+ "CMD",
59
+ "wget",
60
+ "--no-verbose",
61
+ "--tries=1",
62
+ "--spider",
63
+ "http://localhost:4000/graphql",
64
+ ]
65
+ interval: 30s
66
+ timeout: 10s
67
+ retries: 3
68
+
69
+ # 可选:添加 pgAdmin 用于数据库管理
70
+ pgadmin:
71
+ image: dpage/pgadmin4:latest
72
+ environment:
73
+ PGADMIN_DEFAULT_EMAIL: admin@dubhe.com
74
+ PGADMIN_DEFAULT_PASSWORD: admin
75
+ ports:
76
+ - "5050:80"
77
+ volumes:
78
+ - pgadmin_data:/var/lib/pgadmin
79
+ depends_on:
80
+ - postgres
81
+ profiles:
82
+ - tools
83
+
84
+ volumes:
85
+ postgres_data:
86
+ redis_data:
87
+ pgadmin_data:
package/package.json ADDED
@@ -0,0 +1,71 @@
1
+ {
2
+ "name": "@0xobelisk/graphql-server",
3
+ "version": "1.2.0-pre.24",
4
+ "description": "Tookit for interacting with dubhe graphql server",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "keywords": [
8
+ "graphql",
9
+ "postgraphile",
10
+ "sui-indexer",
11
+ "database-adapter",
12
+ "websocket",
13
+ "subscription",
14
+ "postgresql",
15
+ "dynamic"
16
+ ],
17
+ "author": "Dubhe Team",
18
+ "license": "MIT",
19
+ "dependencies": {
20
+ "@graphile-contrib/pg-simplify-inflector": "^6.1.0",
21
+ "@graphile/pg-pubsub": "^4.14.1",
22
+ "@graphile/subscriptions-lds": "^4.14.1",
23
+ "apollo-server-express": "^3.13.0",
24
+ "cors": "^2.8.5",
25
+ "dotenv": "^16.3.1",
26
+ "express": "^4.21.2",
27
+ "graphile-utils": "^4.14.1",
28
+ "graphql": "^15.8.0",
29
+ "graphql-subscriptions": "^2.0.0",
30
+ "graphql-ws": "^5.16.2",
31
+ "node-fetch": "^3.3.2",
32
+ "pg": "^8.11.3",
33
+ "pino": "^8.16.2",
34
+ "pino-pretty": "^10.2.3",
35
+ "postgraphile": "^4.13.0",
36
+ "postgraphile-plugin-connection-filter": "^2.3.0",
37
+ "ws": "^8.14.2"
38
+ },
39
+ "devDependencies": {
40
+ "@types/cors": "^2.8.18",
41
+ "@types/express": "^4.17.23",
42
+ "@types/node": "^20.8.10",
43
+ "@types/pg": "^8.10.7",
44
+ "@types/ws": "^8.5.8",
45
+ "@typescript-eslint/eslint-plugin": "^6.9.1",
46
+ "@typescript-eslint/parser": "^6.9.1",
47
+ "eslint": "^8.52.0",
48
+ "jest": "^29.7.0",
49
+ "nodemon": "^3.1.10",
50
+ "ts-node": "^10.9.1",
51
+ "tsx": "^3.12.10",
52
+ "typescript": "^5.2.2"
53
+ },
54
+ "engines": {
55
+ "node": ">=18.0.0"
56
+ },
57
+ "scripts": {
58
+ "dev": "nodemon --exec ts-node src/index.ts",
59
+ "dev:node": "node --loader tsx/esm src/index.ts",
60
+ "dev:ts-node": "ts-node-esm src/index.ts",
61
+ "build": "tsc",
62
+ "start": "node dist/index.js",
63
+ "test": "jest",
64
+ "test:subscription": "node test-subscription.js",
65
+ "test:enhanced": "node test_enhanced_subscriptions.js",
66
+ "test:all": "node test_all_subscriptions.js",
67
+ "lint": "eslint src/**/*.ts",
68
+ "lint:fix": "eslint src/**/*.ts --fix",
69
+ "type-check": "tsc --noEmit"
70
+ }
71
+ }
package/server.log ADDED
@@ -0,0 +1,62 @@
1
+ [2025-06-06 07:48:10] INFO: dubhe-graphql-server [system]: 🚀 启动 Sui Indexer GraphQL 服务器... {"component":"system","nodeVersion":"v18.20.4","platform":"darwin"}
2
+ [2025-06-06 07:48:10] INFO: dubhe-graphql-server [system]: 正在初始化数据库连接和扫描表结构... {"component":"system","schema":"public","databaseUrl":"postgres://postgres:****@127.0.0.1:5432/postgres"}
3
+ [2025-06-06 07:48:10] INFO: dubhe-graphql-server [database]: 数据库连接成功 {"component":"database","schema":"public"}
4
+ [2025-06-06 07:48:10] INFO: dubhe-graphql-server [database]: 扫描表结构完成 {"component":"database","tableCount":5,"storeTableCount":4,"tableNames":["store_accounts","store_encounter","store_map_config","store_position","table_fields"]}
5
+ [2025-06-06 07:48:10] INFO: dubhe-graphql-server [subscription]: 启动统一实时引擎 (Live Queries + Native WebSocket) {"component":"subscription","enableSubscriptions":"true","availableTableCount":5,"storeTablesCount":4}
6
+ [2025-06-06 07:48:10] INFO: dubhe-graphql-server [system]: 🚀 统一实时引擎启动 {"component":"system","port":4001,"enableLiveQueries":true,"enableNativeWebSocket":true,"tablesCount":5}
7
+ [2025-06-06 07:48:10] INFO: dubhe-graphql-server [server]: 创建 PostGraphile 配置 {"component":"server","endpoint":"/graphql","enableCors":"true","enableSubscriptions":"true"}
8
+ [2025-06-06 07:48:10] INFO: dubhe-graphql-server [subscription]: 创建简化订阅插件 {"component":"subscription","totalTables":5,"storeTables":4,"storeTableNames":["store_accounts","store_encounter","store_map_config","store_position"]}
9
+ [2025-06-06 07:48:10] INFO: dubhe-graphql-server [server]: 初始化服务器管理器 {"component":"server","port":4000,"endpoint":"/graphql","subscriptions":"true","realtimePort":"4001"}
10
+ [2025-06-06 07:48:10] INFO: dubhe-graphql-server [subscription]: PostGraphile Live Queries已启用,无需额外配置 {"component":"subscription"}
11
+ [2025-06-06 07:48:10] INFO: dubhe-graphql-server [server]: 🚀 Sui Indexer GraphQL 服务器启动成功! {"component":"server"}
12
+ [2025-06-06 07:48:10] INFO: dubhe-graphql-server [server]: 服务器地址 {"component":"server","url":"http://localhost:4000"}
13
+ [2025-06-06 07:48:10] INFO: dubhe-graphql-server [server]: GraphQL API {"component":"server","url":"http://localhost:4000/graphql"}
14
+ [2025-06-06 07:48:10] INFO: dubhe-graphql-server [server]: 增强版 GraphQL Playground {"component":"server","url":"http://localhost:4000/playground","features":["现代化界面","Schema Explorer","代码导出"],"note":"旧路径 /graphiql 会自动重定向到 /playground"}
15
+ [2025-06-06 07:48:10] INFO: dubhe-graphql-server [server]: WebSocket 订阅 {"component":"server","url":"ws://localhost:4000/graphql"}
16
+ [2025-06-06 07:48:10] INFO: dubhe-graphql-server [server]: 服务器配置 {"component":"server","environment":"development","schema":"public","totalTables":5,"storeTables":4,"systemTables":1,"cors":"启用","subscriptions":"启用"}
17
+ [2025-06-06 07:48:10] INFO: dubhe-graphql-server [system]: 💡 访问根路径查看详细信息和使用指南 {"component":"system"}
18
+ [2025-06-06 07:48:10] INFO: dubhe-graphql-server [system]: 按 Ctrl+C 停止服务器 {"component":"system"}
19
+ [2025-06-06 07:48:10] INFO: dubhe-graphql-server [performance]: 服务器启动 completed {"component":"performance","duration":"81ms","port":4000,"tableCount":5,"nodeEnv":"development"}
20
+ [2025-06-06 07:48:10] INFO: dubhe-graphql-server [database]: 开始监听数据库通道 {"component":"database","channel":"table_change_store_accounts"}
21
+ [2025-06-06 07:48:10] INFO: dubhe-graphql-server [database]: 开始监听数据库通道 {"component":"database","channel":"table_change_store_encounter"}
22
+ [2025-06-06 07:48:10] INFO: dubhe-graphql-server [database]: 开始监听数据库通道 {"component":"database","channel":"table_change_store_map_config"}
23
+ [2025-06-06 07:48:10] INFO: dubhe-graphql-server [database]: 开始监听数据库通道 {"component":"database","channel":"table_change_store_position"}
24
+ [2025-06-06 07:48:10] INFO: dubhe-graphql-server [database]: 开始监听数据库通道 {"component":"database","channel":"table_change_table_fields"}
25
+ [2025-06-06 07:48:10] INFO: dubhe-graphql-server [database]: ✅ 数据库变更监听器设置完成 {"component":"database"}
26
+ 🔍 原始字段列表: [
27
+ 'query',
28
+ 'nodeId',
29
+ 'node',
30
+ 'allStoreAccounts',
31
+ 'allStoreEncounters',
32
+ 'allStoreMapConfigs',
33
+ 'allStorePositions',
34
+ 'allTableFields',
35
+ 'storeAccountByAssetIdAndAccount',
36
+ 'storeEncounterByPlayer',
37
+ 'storePositionByPlayer',
38
+ 'tableFieldByTableNameAndFieldName',
39
+ 'storeAccount',
40
+ 'storeEncounter',
41
+ 'storePosition',
42
+ 'tableField'
43
+ ]
44
+ 🔄 字段重命名统计: { '原始字段数': 16, '最终字段数': 16, '重命名字段数': 10 }
45
+ 📝 重命名映射: {
46
+ allStoreAccounts: 'accounts',
47
+ allStoreEncounters: 'encounters',
48
+ allStoreMapConfigs: 'mapConfigs',
49
+ allStorePositions: 'positions',
50
+ storeAccountByAssetIdAndAccount: 'accountByAssetIdAndAccount',
51
+ storeEncounterByPlayer: 'encounterByPlayer',
52
+ storePositionByPlayer: 'positionByPlayer',
53
+ storeAccount: 'account',
54
+ storeEncounter: 'encounter',
55
+ storePosition: 'position'
56
+ }
57
+ [2025-06-06 07:48:10] INFO: dubhe-graphql-server [subscription]: 启用PostGraphile原生Live Queries功能 {"component":"subscription"}
58
+ [2025-06-06 07:48:31] INFO: dubhe-graphql-server [performance]: GraphQL GET completed {"component":"performance","duration":"1ms","endpoint":"/graphql"}
59
+ [2025-06-06 07:51:36] INFO: dubhe-graphql-server [system]: 收到 SIGTERM 信号,开始优雅关闭服务器... {"component":"system"}
60
+ [2025-06-06 07:51:36] INFO: dubhe-graphql-server [system]: 🛑 开始关闭统一实时引擎... {"component":"system"}
61
+ [2025-06-06 07:51:36] INFO: dubhe-graphql-server [system]: ✅ 统一实时引擎已关闭 {"component":"system"}
62
+ [2025-06-06 07:51:36] INFO: dubhe-graphql-server [system]: ⏹️ 正在关闭服务器... {"component":"system"}
@@ -0,0 +1,186 @@
1
+ // Subscription configuration manager - supports dynamic configuration for three subscription modes
2
+
3
+ export interface SubscriptionCapabilities {
4
+ liveQueries: boolean;
5
+ pgSubscriptions: boolean;
6
+ nativeWebSocket: boolean;
7
+ }
8
+
9
+ export interface SubscriptionConfig {
10
+ // Basic configuration
11
+ enableSubscriptions: boolean;
12
+
13
+ // Capability configuration
14
+ capabilities: SubscriptionCapabilities;
15
+
16
+ // Database configuration detection
17
+ walLevel: 'minimal' | 'replica' | 'logical';
18
+ pgVersion: string;
19
+
20
+ // Port configuration
21
+ graphqlPort: number;
22
+ websocketPort?: number;
23
+
24
+ // Performance configuration
25
+ maxConnections: number;
26
+ heartbeatInterval: number;
27
+
28
+ // Debug configuration
29
+ enableNotificationLogging: boolean;
30
+ enablePerformanceMetrics: boolean;
31
+ }
32
+
33
+ export class SubscriptionConfigManager {
34
+ private config: SubscriptionConfig;
35
+
36
+ constructor(envVars: Record<string, string>) {
37
+ this.config = this.parseEnvironmentVariables(envVars);
38
+ }
39
+
40
+ // Parse configuration from environment variables
41
+ private parseEnvironmentVariables(env: Record<string, string>): SubscriptionConfig {
42
+ const enableSubscriptions = env.ENABLE_SUBSCRIPTIONS !== 'false'; // Default enabled unless explicitly set to false
43
+
44
+ // Auto-detect WAL level (in actual applications, query through database)
45
+ const walLevel = this.detectWalLevel(env.DATABASE_URL);
46
+
47
+ // Determine capabilities based on WAL level and environment variables - default enable all features
48
+ const capabilities: SubscriptionCapabilities = {
49
+ liveQueries:
50
+ enableSubscriptions && env.ENABLE_LIVE_QUERIES !== 'false' && walLevel === 'logical',
51
+
52
+ pgSubscriptions: enableSubscriptions && env.ENABLE_PG_SUBSCRIPTIONS !== 'false',
53
+
54
+ nativeWebSocket: enableSubscriptions && env.ENABLE_NATIVE_WEBSOCKET !== 'false'
55
+ };
56
+
57
+ return {
58
+ enableSubscriptions,
59
+ capabilities,
60
+ walLevel,
61
+ pgVersion: '13+', // In actual applications, get through query
62
+
63
+ graphqlPort: parseInt(env.PORT || '4000'),
64
+ websocketPort: env.REALTIME_PORT ? parseInt(env.REALTIME_PORT) : undefined,
65
+
66
+ maxConnections: parseInt(env.MAX_SUBSCRIPTION_CONNECTIONS || '1000'),
67
+ heartbeatInterval: parseInt(env.SUBSCRIPTION_HEARTBEAT_INTERVAL || '30000'),
68
+
69
+ enableNotificationLogging: env.DEBUG_NOTIFICATIONS === 'true',
70
+ enablePerformanceMetrics: env.ENABLE_SUBSCRIPTION_METRICS === 'true'
71
+ };
72
+ }
73
+
74
+ // Detect database WAL level
75
+ private detectWalLevel(databaseUrl?: string): 'minimal' | 'replica' | 'logical' {
76
+ // In actual applications should query database
77
+ // SELECT setting FROM pg_settings WHERE name = 'wal_level';
78
+
79
+ // Currently return default value
80
+ return 'replica';
81
+ }
82
+
83
+ // Get current configuration
84
+ getConfig(): SubscriptionConfig {
85
+ return { ...this.config };
86
+ }
87
+
88
+ // Check if specific capability is available
89
+ isCapabilityEnabled(capability: keyof SubscriptionCapabilities): boolean {
90
+ return this.config.capabilities[capability];
91
+ }
92
+
93
+ // Get recommended subscription method
94
+ getRecommendedSubscriptionMethod(): string {
95
+ if (this.config.capabilities.liveQueries) {
96
+ return 'live-queries';
97
+ } else if (this.config.capabilities.pgSubscriptions) {
98
+ return 'pg-subscriptions';
99
+ } else if (this.config.capabilities.nativeWebSocket) {
100
+ return 'native-websocket';
101
+ } else {
102
+ return 'none';
103
+ }
104
+ }
105
+
106
+ // Generate client configuration
107
+ generateClientConfig() {
108
+ const baseUrl = `http://localhost:${this.config.graphqlPort}`;
109
+
110
+ return {
111
+ graphqlEndpoint: `${baseUrl}/graphql`,
112
+ subscriptionEndpoint:
113
+ this.config.capabilities.pgSubscriptions || this.config.capabilities.liveQueries
114
+ ? `ws://localhost:${this.config.graphqlPort}/graphql`
115
+ : undefined,
116
+ nativeWebSocketEndpoint: this.config.capabilities.nativeWebSocket
117
+ ? `ws://localhost:${this.config.websocketPort || this.config.graphqlPort}`
118
+ : undefined,
119
+ capabilities: this.config.capabilities,
120
+ recommendedMethod: this.getRecommendedSubscriptionMethod()
121
+ };
122
+ }
123
+
124
+ // Generate PostGraphile configuration - simplified version, only keep listen subscriptions
125
+ generatePostGraphileConfig() {
126
+ return {
127
+ subscriptions: this.config.enableSubscriptions,
128
+ live: false, // Disable live queries, only use listen subscriptions
129
+ simpleSubscriptions: this.config.capabilities.pgSubscriptions,
130
+
131
+ // Performance configuration - optimized for listen subscriptions
132
+ pgSettings: {
133
+ statement_timeout: '30s',
134
+ default_transaction_isolation: 'read committed'
135
+ },
136
+
137
+ // Monitoring configuration
138
+ allowExplain: this.config.enablePerformanceMetrics,
139
+ disableQueryLog: !this.config.enableNotificationLogging
140
+ };
141
+ }
142
+
143
+ // Generate environment variable documentation
144
+ generateDocumentation(): string {
145
+ return `
146
+ # 📡 Subscription System Configuration Guide
147
+
148
+ ## Basic Configuration
149
+ ENABLE_SUBSCRIPTIONS=false # Disable subscription features (default enabled, set to false to disable)
150
+
151
+ ## Capability Configuration (optional, auto-detect by default)
152
+ ENABLE_LIVE_QUERIES=true # Enable @live directive (requires wal_level=logical)
153
+ ENABLE_PG_SUBSCRIPTIONS=true # Enable PostgreSQL subscriptions
154
+ ENABLE_NATIVE_WEBSOCKET=true # Enable native WebSocket
155
+
156
+ ## Port Configuration
157
+ PORT=4000 # GraphQL port
158
+ REALTIME_PORT=4001 # Native WebSocket port (optional)
159
+
160
+ ## Performance Configuration
161
+ MAX_SUBSCRIPTION_CONNECTIONS=1000 # Maximum connections
162
+ SUBSCRIPTION_HEARTBEAT_INTERVAL=30000 # Heartbeat interval (ms)
163
+
164
+ ## Debug Configuration
165
+ DEBUG_NOTIFICATIONS=false # Notification logging
166
+ ENABLE_SUBSCRIPTION_METRICS=false # Performance metrics
167
+
168
+ ## Current Configuration Status:
169
+ - Subscription Features: ${this.config.enableSubscriptions ? '✅ Enabled' : '❌ Disabled'}
170
+ - Live Queries: ${this.config.capabilities.liveQueries ? '✅ Available' : '❌ Not Available'}
171
+ - PG Subscriptions: ${
172
+ this.config.capabilities.pgSubscriptions ? '✅ Available' : '❌ Not Available'
173
+ }
174
+ - Native WebSocket: ${
175
+ this.config.capabilities.nativeWebSocket ? '✅ Available' : '❌ Not Available'
176
+ }
177
+ - WAL Level: ${this.config.walLevel}
178
+ - Recommended Method: ${this.getRecommendedSubscriptionMethod()}
179
+ `;
180
+ }
181
+ }
182
+
183
+ // Export singleton instance
184
+ export const subscriptionConfig = new SubscriptionConfigManager(
185
+ process.env as Record<string, string>
186
+ );
package/src/index.ts ADDED
@@ -0,0 +1,239 @@
1
+ import { postgraphile } from 'postgraphile';
2
+ import { Pool } from 'pg';
3
+ import * as dotenv from 'dotenv';
4
+ import {
5
+ dbLogger,
6
+ serverLogger,
7
+ systemLogger,
8
+ subscriptionLogger,
9
+ logPerformance
10
+ } from './utils/logger';
11
+ import {
12
+ DatabaseIntrospector,
13
+ createPostGraphileConfig,
14
+ PostGraphileConfigOptions,
15
+ WelcomePageConfig
16
+ } from './plugins';
17
+ import { EnhancedServerManager } from './plugins/enhanced-server-manager';
18
+ import { subscriptionConfig } from './config/subscription-config';
19
+ import {
20
+ generateStoreTablesInfo,
21
+ createUniversalSubscriptionsPlugin
22
+ } from './universal-subscriptions';
23
+
24
+ // Load environment variables
25
+ dotenv.config();
26
+
27
+ // Environment variable configuration
28
+ const {
29
+ DATABASE_URL = 'postgres://postgres:postgres@127.0.0.1:5432/postgres',
30
+ PORT = 4000,
31
+ NODE_ENV = 'development',
32
+ GRAPHQL_ENDPOINT = '/graphql',
33
+ PG_SCHEMA = 'public',
34
+ ENABLE_CORS = 'true'
35
+ } = process.env;
36
+
37
+ // Subscription feature is enabled by default unless explicitly set to false
38
+ const ENABLE_SUBSCRIPTIONS = process.env.ENABLE_SUBSCRIPTIONS !== 'false' ? 'true' : 'false';
39
+
40
+ // Create database connection pool
41
+ const pgPool = new Pool({
42
+ connectionString: DATABASE_URL
43
+ });
44
+
45
+ // Start server
46
+ const startServer = async (): Promise<void> => {
47
+ const startTime = Date.now();
48
+
49
+ try {
50
+ // 1. Test database connection and scan table structure
51
+ systemLogger.info('Initializing database connection and scanning table structure...', {
52
+ schema: PG_SCHEMA,
53
+ databaseUrl: DATABASE_URL.replace(/:[^:]*@/, ':****@') // Hide password
54
+ });
55
+
56
+ const introspector = new DatabaseIntrospector(pgPool, PG_SCHEMA);
57
+
58
+ const isConnected = await introspector.testConnection();
59
+ if (!isConnected) {
60
+ throw new Error('Database connection failed');
61
+ }
62
+ dbLogger.info('Database connection successful', { schema: PG_SCHEMA });
63
+
64
+ const allTables = await introspector.getAllTables();
65
+ const tableNames = allTables.map((t) => t.table_name);
66
+
67
+ dbLogger.info('Table structure scan completed', {
68
+ tableCount: allTables.length,
69
+ storeTableCount: tableNames.filter((name) => name.startsWith('store_')).length,
70
+ tableNames: tableNames.slice(0, 10) // Only show first 10 table names
71
+ });
72
+
73
+ // 2. Display subscription configuration status
74
+ const config = subscriptionConfig.getConfig();
75
+ subscriptionLogger.info('📡 Subscription system configuration status', {
76
+ enableSubscriptions: config.enableSubscriptions,
77
+ capabilities: {
78
+ pgSubscriptions: config.capabilities.pgSubscriptions
79
+ },
80
+ recommendedMethod: 'pg-subscriptions',
81
+ walLevel: config.walLevel
82
+ });
83
+
84
+ // 3. Pre-generate store table information for dynamic queries
85
+ subscriptionLogger.info('Pre-generating store table information for tool queries...');
86
+ const storeTablesInfo = await generateStoreTablesInfo(pgPool);
87
+ const storeTableNames = Object.keys(storeTablesInfo);
88
+
89
+ subscriptionLogger.info(`Discovered store tables: ${storeTableNames.join(', ')}`);
90
+
91
+ // 4. Create PostGraphile configuration
92
+ const postgraphileConfigOptions: PostGraphileConfigOptions = {
93
+ port: PORT,
94
+ nodeEnv: NODE_ENV,
95
+ graphqlEndpoint: GRAPHQL_ENDPOINT,
96
+ enableSubscriptions: ENABLE_SUBSCRIPTIONS,
97
+ enableCors: ENABLE_CORS,
98
+ databaseUrl: DATABASE_URL,
99
+ availableTables: tableNames
100
+ };
101
+
102
+ serverLogger.info('Creating PostGraphile configuration', {
103
+ endpoint: GRAPHQL_ENDPOINT,
104
+ enableCors: ENABLE_CORS,
105
+ enableSubscriptions: ENABLE_SUBSCRIPTIONS
106
+ });
107
+
108
+ // Use simplified configuration
109
+ const postgraphileConfig = {
110
+ ...createPostGraphileConfig(postgraphileConfigOptions),
111
+ ...subscriptionConfig.generatePostGraphileConfig()
112
+ };
113
+
114
+ // Add tools query plugin
115
+ const toolsPlugin = createUniversalSubscriptionsPlugin(storeTablesInfo);
116
+ postgraphileConfig.appendPlugins = [...(postgraphileConfig.appendPlugins || []), toolsPlugin];
117
+
118
+ // 5. Create PostGraphile middleware
119
+ console.log('🔧 Creating PostGraphile middleware...');
120
+ const postgraphileMiddleware = postgraphile(pgPool, PG_SCHEMA, {
121
+ ...postgraphileConfig
122
+ });
123
+ console.log('✅ PostGraphile middleware creation completed:', typeof postgraphileMiddleware);
124
+
125
+ // 6. Configure welcome page
126
+ const welcomeConfig: WelcomePageConfig = {
127
+ port: PORT,
128
+ graphqlEndpoint: GRAPHQL_ENDPOINT,
129
+ nodeEnv: NODE_ENV,
130
+ schema: PG_SCHEMA,
131
+ enableCors: ENABLE_CORS,
132
+ enableSubscriptions: ENABLE_SUBSCRIPTIONS
133
+ };
134
+
135
+ // 7. Create Express server manager
136
+ const serverManager = new EnhancedServerManager();
137
+
138
+ // 8. Create Express server
139
+ await serverManager.createEnhancedServer({
140
+ postgraphileMiddleware,
141
+ pgPool,
142
+ tableNames,
143
+ databaseUrl: DATABASE_URL,
144
+ allTables,
145
+ welcomeConfig,
146
+ postgraphileConfigOptions
147
+ });
148
+
149
+ // 9. Start Express server
150
+ await serverManager.startServer();
151
+
152
+ logPerformance('Express server startup', startTime, {
153
+ port: PORT,
154
+ tableCount: allTables.length,
155
+ storeTableCount: storeTableNames.length,
156
+ nodeEnv: NODE_ENV,
157
+ framework: 'Express',
158
+ capabilities: {
159
+ pgSubscriptions: config.capabilities.pgSubscriptions
160
+ }
161
+ });
162
+
163
+ // 10. Display usage instructions
164
+ if (NODE_ENV === 'development') {
165
+ console.log('\n' + '='.repeat(80));
166
+ console.log('📖 Quick Access (Express Architecture):');
167
+ console.log(`Visit http://localhost:${PORT}/ to view homepage`);
168
+ console.log(`Visit http://localhost:${PORT}/playground to use GraphQL Playground`);
169
+ console.log(`Visit http://localhost:${PORT}/health to check server status`);
170
+ console.log(`Visit http://localhost:${PORT}/subscription-config to get client configuration`);
171
+ console.log(`Visit http://localhost:${PORT}/subscription-docs to view configuration guide`);
172
+ console.log('='.repeat(80) + '\n');
173
+ }
174
+
175
+ // 11. Set up simple and direct shutdown handling
176
+ let isShuttingDown = false;
177
+ const quickShutdown = (signal: string) => {
178
+ if (isShuttingDown) {
179
+ systemLogger.info('⚡ Force exiting process...');
180
+ process.exit(0);
181
+ }
182
+
183
+ isShuttingDown = true;
184
+ systemLogger.info(`🛑 Received ${signal} signal, shutting down Express server...`);
185
+
186
+ // Set 1 second force exit timeout
187
+ setTimeout(() => {
188
+ systemLogger.info('⚡ Quick exit');
189
+ process.exit(0);
190
+ }, 1000);
191
+
192
+ // Try to shutdown Express server quickly
193
+ serverManager.quickShutdown().finally(() => {
194
+ process.exit(0);
195
+ });
196
+ };
197
+
198
+ process.on('SIGINT', () => quickShutdown('SIGINT'));
199
+ process.on('SIGTERM', () => quickShutdown('SIGTERM'));
200
+
201
+ // Simplified exception handling
202
+ process.on('unhandledRejection', (reason) => {
203
+ console.error('❌ Unhandled Promise rejection:', reason);
204
+ });
205
+
206
+ process.on('uncaughtException', (error) => {
207
+ console.error('❌ Uncaught exception:', error.message);
208
+ process.exit(1);
209
+ });
210
+ } catch (error) {
211
+ systemLogger.error('Failed to start Express server', error, {
212
+ databaseUrl: DATABASE_URL.replace(/:[^:]*@/, ':****@'),
213
+ schema: PG_SCHEMA,
214
+ port: PORT
215
+ });
216
+
217
+ systemLogger.info('💡 Possible causes:');
218
+ systemLogger.info('1. Database connection failed - check DATABASE_URL');
219
+ systemLogger.info(
220
+ '2. Expected table structure not found in database - ensure sui-rust-indexer is running'
221
+ );
222
+ systemLogger.info('3. Permission issues - ensure database user has sufficient permissions');
223
+ systemLogger.info('4. Missing dependencies - run pnpm install');
224
+
225
+ // Display subscription configuration help
226
+ console.log('\n' + subscriptionConfig.generateDocumentation());
227
+
228
+ process.exit(1);
229
+ }
230
+ };
231
+
232
+ // Start application
233
+ systemLogger.info('🚀 Starting Sui Indexer GraphQL Server (Express Architecture)...', {
234
+ nodeVersion: process.version,
235
+ platform: process.platform,
236
+ pid: process.pid
237
+ });
238
+
239
+ startServer();