@0xobelisk/graphql-server 1.2.0-pre.100
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.
- package/.turbo/turbo-build.log +8 -0
- package/DUAL_POOL_CONFIG.md +188 -0
- package/Dockerfile +35 -0
- package/LICENSE +92 -0
- package/README.md +487 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +206 -0
- package/dist/cli.js.map +1 -0
- package/dist/config/subscription-config.d.ts +80 -0
- package/dist/config/subscription-config.d.ts.map +1 -0
- package/dist/config/subscription-config.js +158 -0
- package/dist/config/subscription-config.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -0
- package/dist/plugins/all-fields-filter-plugin.d.ts +4 -0
- package/dist/plugins/all-fields-filter-plugin.d.ts.map +1 -0
- package/dist/plugins/all-fields-filter-plugin.js +132 -0
- package/dist/plugins/all-fields-filter-plugin.js.map +1 -0
- package/dist/plugins/database-introspector.d.ts +23 -0
- package/dist/plugins/database-introspector.d.ts.map +1 -0
- package/dist/plugins/database-introspector.js +96 -0
- package/dist/plugins/database-introspector.js.map +1 -0
- package/dist/plugins/enhanced-playground.d.ts +9 -0
- package/dist/plugins/enhanced-playground.d.ts.map +1 -0
- package/dist/plugins/enhanced-playground.js +113 -0
- package/dist/plugins/enhanced-playground.js.map +1 -0
- package/dist/plugins/enhanced-server-manager.d.ts +29 -0
- package/dist/plugins/enhanced-server-manager.d.ts.map +1 -0
- package/dist/plugins/enhanced-server-manager.js +262 -0
- package/dist/plugins/enhanced-server-manager.js.map +1 -0
- package/dist/plugins/index.d.ts +9 -0
- package/dist/plugins/index.d.ts.map +1 -0
- package/dist/plugins/index.js +26 -0
- package/dist/plugins/index.js.map +1 -0
- package/dist/plugins/postgraphile-config.d.ts +94 -0
- package/dist/plugins/postgraphile-config.d.ts.map +1 -0
- package/dist/plugins/postgraphile-config.js +138 -0
- package/dist/plugins/postgraphile-config.js.map +1 -0
- package/dist/plugins/query-filter.d.ts +4 -0
- package/dist/plugins/query-filter.d.ts.map +1 -0
- package/dist/plugins/query-filter.js +42 -0
- package/dist/plugins/query-filter.js.map +1 -0
- package/dist/plugins/simple-naming.d.ts +4 -0
- package/dist/plugins/simple-naming.d.ts.map +1 -0
- package/dist/plugins/simple-naming.js +79 -0
- package/dist/plugins/simple-naming.js.map +1 -0
- package/dist/plugins/welcome-page.d.ts +11 -0
- package/dist/plugins/welcome-page.d.ts.map +1 -0
- package/dist/plugins/welcome-page.js +203 -0
- package/dist/plugins/welcome-page.js.map +1 -0
- package/dist/server.d.ts +21 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +265 -0
- package/dist/server.js.map +1 -0
- package/dist/universal-subscriptions.d.ts +32 -0
- package/dist/universal-subscriptions.d.ts.map +1 -0
- package/dist/universal-subscriptions.js +318 -0
- package/dist/universal-subscriptions.js.map +1 -0
- package/dist/utils/logger/index.d.ts +80 -0
- package/dist/utils/logger/index.d.ts.map +1 -0
- package/dist/utils/logger/index.js +230 -0
- package/dist/utils/logger/index.js.map +1 -0
- package/docker-compose.yml +46 -0
- package/eslint.config.mjs +3 -0
- package/package.json +78 -0
- package/src/cli.ts +232 -0
- package/src/config/subscription-config.ts +243 -0
- package/src/index.ts +11 -0
- package/src/plugins/README.md +138 -0
- package/src/plugins/all-fields-filter-plugin.ts +158 -0
- package/src/plugins/database-introspector.ts +126 -0
- package/src/plugins/enhanced-playground.ts +121 -0
- package/src/plugins/enhanced-server-manager.ts +314 -0
- package/src/plugins/index.ts +9 -0
- package/src/plugins/postgraphile-config.ts +182 -0
- package/src/plugins/query-filter.ts +50 -0
- package/src/plugins/simple-naming.ts +105 -0
- package/src/plugins/welcome-page.ts +218 -0
- package/src/server.ts +324 -0
- package/src/universal-subscriptions.ts +397 -0
- package/src/utils/logger/README.md +209 -0
- package/src/utils/logger/index.ts +275 -0
- package/sui-indexer-schema.graphql +3691 -0
- package/tsconfig.json +28 -0
package/README.md
ADDED
|
@@ -0,0 +1,487 @@
|
|
|
1
|
+
# Dubhe GraphQL Server
|
|
2
|
+
|
|
3
|
+
The Dubhe GraphQL Server is an intelligent universal server adapter that automatically connects to databases created by `dubhe-indexer` and dynamically generates complete GraphQL APIs. Built on PostGraphile, it provides advanced filtering, real-time subscriptions, and comprehensive data access capabilities with zero configuration required.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
Install the package in your project:
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
pnpm install @0xobelisk/graphql-server
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Or install globally for CLI usage:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm install -g @0xobelisk/graphql-server
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Requirements
|
|
20
|
+
|
|
21
|
+
- Node.js 22.0.0+
|
|
22
|
+
- PostgreSQL database (managed by dubhe-indexer)
|
|
23
|
+
- TypeScript 5.0+
|
|
24
|
+
|
|
25
|
+
## Quick Start
|
|
26
|
+
|
|
27
|
+
### Using the CLI
|
|
28
|
+
|
|
29
|
+
The server provides a comprehensive CLI interface for configuration:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
# Start with default configuration
|
|
33
|
+
dubhe-graphql-server start
|
|
34
|
+
|
|
35
|
+
# Start with custom configuration
|
|
36
|
+
dubhe-graphql-server start --port 4000 --database-url postgres://user:pass@localhost:5432/db
|
|
37
|
+
|
|
38
|
+
# Development mode with debug logging
|
|
39
|
+
dubhe-graphql-server start --debug --enable-metrics
|
|
40
|
+
|
|
41
|
+
# Production mode
|
|
42
|
+
dubhe-graphql-server start --env production --no-cors
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### CLI Options
|
|
46
|
+
|
|
47
|
+
All configuration can be managed through CLI arguments or environment variables:
|
|
48
|
+
|
|
49
|
+
| Option | Environment Variable | Default | Description |
|
|
50
|
+
| --------------------------- | ------------------------- | ------------------------------------------------------ | ----------------------------------- |
|
|
51
|
+
| `--port, -p` | `PORT` | `4000` | Server port |
|
|
52
|
+
| `--database-url, -d` | `DATABASE_URL` | `postgres://postgres:postgres@127.0.0.1:5432/postgres` | Database connection URL |
|
|
53
|
+
| `--schema, -s` | `PG_SCHEMA` | `public` | PostgreSQL schema name |
|
|
54
|
+
| `--endpoint, -e` | `GRAPHQL_ENDPOINT` | `/graphql` | GraphQL endpoint path |
|
|
55
|
+
| `--cors` | `ENABLE_CORS` | `true` | Enable CORS |
|
|
56
|
+
| `--subscriptions` | `ENABLE_SUBSCRIPTIONS` | `true` | Enable GraphQL subscriptions |
|
|
57
|
+
| `--env` | `NODE_ENV` | `development` | Environment mode |
|
|
58
|
+
| `--debug` | `DEBUG` | `false` | Enable debug mode (verbose logging) |
|
|
59
|
+
| `--query-timeout` | `QUERY_TIMEOUT` | `30000` | GraphQL query timeout (ms) |
|
|
60
|
+
| `--max-connections` | `MAX_CONNECTIONS` | `1000` | Maximum database connections |
|
|
61
|
+
| `--heartbeat-interval` | `HEARTBEAT_INTERVAL` | `30000` | WebSocket heartbeat interval (ms) |
|
|
62
|
+
| `--enable-metrics` | `ENABLE_METRICS` | `false` | Enable performance metrics |
|
|
63
|
+
| `--enable-live-queries` | `ENABLE_LIVE_QUERIES` | `true` | Enable GraphQL live queries |
|
|
64
|
+
| `--enable-pg-subscriptions` | `ENABLE_PG_SUBSCRIPTIONS` | `true` | Enable PostgreSQL subscriptions |
|
|
65
|
+
| `--enable-native-websocket` | `ENABLE_NATIVE_WEBSOCKET` | `true` | Enable native WebSocket support |
|
|
66
|
+
| `--realtime-port` | `REALTIME_PORT` | `undefined` | Realtime WebSocket port |
|
|
67
|
+
|
|
68
|
+
### Environment Configuration (Alternative)
|
|
69
|
+
|
|
70
|
+
You can also use a `.env` file instead of CLI arguments:
|
|
71
|
+
|
|
72
|
+
```env
|
|
73
|
+
# Database configuration (connect to dubhe-indexer database)
|
|
74
|
+
DATABASE_URL=postgres://username:password@localhost:5432/sui_indexer_db
|
|
75
|
+
|
|
76
|
+
# Server configuration
|
|
77
|
+
PORT=4000
|
|
78
|
+
NODE_ENV=development
|
|
79
|
+
|
|
80
|
+
# GraphQL configuration
|
|
81
|
+
GRAPHQL_ENDPOINT=/graphql
|
|
82
|
+
PG_SCHEMA=public
|
|
83
|
+
|
|
84
|
+
# Feature toggles
|
|
85
|
+
ENABLE_CORS=true
|
|
86
|
+
ENABLE_SUBSCRIPTIONS=true
|
|
87
|
+
|
|
88
|
+
# Performance settings
|
|
89
|
+
QUERY_TIMEOUT=30000
|
|
90
|
+
MAX_CONNECTIONS=1000
|
|
91
|
+
HEARTBEAT_INTERVAL=30000
|
|
92
|
+
|
|
93
|
+
# Debug and monitoring
|
|
94
|
+
DEBUG=false
|
|
95
|
+
ENABLE_METRICS=false
|
|
96
|
+
|
|
97
|
+
# Subscription capabilities
|
|
98
|
+
ENABLE_LIVE_QUERIES=true
|
|
99
|
+
ENABLE_PG_SUBSCRIPTIONS=true
|
|
100
|
+
ENABLE_NATIVE_WEBSOCKET=true
|
|
101
|
+
REALTIME_PORT=4001
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## Core Features
|
|
105
|
+
|
|
106
|
+
### Intelligent Database Adaptation
|
|
107
|
+
|
|
108
|
+
The server automatically scans and adapts to your database structure:
|
|
109
|
+
|
|
110
|
+
- **Dynamic Scanning**: Automatically discovers all tables created by `dubhe-indexer`
|
|
111
|
+
- **PostGraphile Powered**: Generates GraphQL APIs based on database schema
|
|
112
|
+
- **Zero Configuration**: No manual schema definition required
|
|
113
|
+
- **Real-time Schema Updates**: Automatically adapts to database changes
|
|
114
|
+
|
|
115
|
+
### Plugin Architecture
|
|
116
|
+
|
|
117
|
+
The server uses a modular plugin architecture:
|
|
118
|
+
|
|
119
|
+
- **Database Introspector**: Scans and analyzes database table structures
|
|
120
|
+
- **Welcome Page Generator**: Creates informative server homepage
|
|
121
|
+
- **PostGraphile Configuration**: Manages GraphQL API generation
|
|
122
|
+
- **Subscription Manager**: Handles real-time WebSocket connections
|
|
123
|
+
- **Enhanced Server Manager**: Manages HTTP and WebSocket servers
|
|
124
|
+
|
|
125
|
+
### Advanced Filtering
|
|
126
|
+
|
|
127
|
+
The server provides comprehensive filtering capabilities through the `postgraphile-plugin-connection-filter` plugin:
|
|
128
|
+
|
|
129
|
+
- **Rich Operators**: Supports 20+ filtering operators (eq, gt, lt, in, like, etc.)
|
|
130
|
+
- **Logical Combinations**: AND, OR, NOT operations for complex queries
|
|
131
|
+
- **Type-aware Filtering**: Automatic operator selection based on field types
|
|
132
|
+
- **Case-insensitive Search**: Text search with case sensitivity options
|
|
133
|
+
- **Null Handling**: Explicit null and not-null filtering
|
|
134
|
+
|
|
135
|
+
```graphql
|
|
136
|
+
# Basic filtering
|
|
137
|
+
query GetHighValueAccounts {
|
|
138
|
+
accounts(filter: { balance: { gt: "1000" } }) {
|
|
139
|
+
nodes {
|
|
140
|
+
assetId
|
|
141
|
+
account
|
|
142
|
+
balance
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
# Complex logical combinations
|
|
148
|
+
query GetComplexFilteredAccounts {
|
|
149
|
+
accounts(
|
|
150
|
+
filter: {
|
|
151
|
+
and: [
|
|
152
|
+
{ or: [{ balance: { gt: "1000" } }, { assetId: { like: "%special%" } }] }
|
|
153
|
+
{ not: { account: { includesInsensitive: "test" } } }
|
|
154
|
+
]
|
|
155
|
+
}
|
|
156
|
+
) {
|
|
157
|
+
nodes {
|
|
158
|
+
assetId
|
|
159
|
+
account
|
|
160
|
+
balance
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
### Real-time Subscriptions
|
|
167
|
+
|
|
168
|
+
Advanced WebSocket support powered by PostgreSQL LISTEN/NOTIFY:
|
|
169
|
+
|
|
170
|
+
- **Live Queries**: PostGraphile Live Queries for real-time data updates
|
|
171
|
+
- **PostgreSQL Subscriptions**: Native database change notifications
|
|
172
|
+
- **WebSocket Transport**: Unified WebSocket endpoint for all subscriptions
|
|
173
|
+
- **Connection Management**: Automatic heartbeat and connection recovery
|
|
174
|
+
- **Universal Subscriptions**: Custom subscription plugin for store tables
|
|
175
|
+
|
|
176
|
+
```graphql
|
|
177
|
+
# Subscribe to specific table changes
|
|
178
|
+
subscription OnAccountChanges {
|
|
179
|
+
accounts(first: 10, orderBy: [CREATED_AT_TIMESTAMP_DESC]) {
|
|
180
|
+
nodes {
|
|
181
|
+
assetId
|
|
182
|
+
account
|
|
183
|
+
balance
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
## Access Endpoints
|
|
190
|
+
|
|
191
|
+
After starting the server, you can access:
|
|
192
|
+
|
|
193
|
+
- **Welcome Page**: `http://localhost:4000/` - Server information and table overview
|
|
194
|
+
- **GraphQL Playground**: `http://localhost:4000/playground` - Modern GraphQL IDE
|
|
195
|
+
- **GraphQL API**: `http://localhost:4000/graphql` - API endpoint
|
|
196
|
+
- **Health Check**: `http://localhost:4000/health` - Server health status
|
|
197
|
+
- **Subscription Config**: `http://localhost:4000/subscription-config` - Client configuration
|
|
198
|
+
- **WebSocket**: `ws://localhost:4000/graphql` - Subscription endpoint
|
|
199
|
+
|
|
200
|
+
## Deployment
|
|
201
|
+
|
|
202
|
+
### Development
|
|
203
|
+
|
|
204
|
+
```bash
|
|
205
|
+
# Start development server
|
|
206
|
+
pnpm install
|
|
207
|
+
dubhe-graphql-server start --debug --enable-metrics
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
### Production
|
|
211
|
+
|
|
212
|
+
```bash
|
|
213
|
+
# Build the project
|
|
214
|
+
pnpm install
|
|
215
|
+
pnpm build
|
|
216
|
+
|
|
217
|
+
# Start production server
|
|
218
|
+
dubhe-graphql-server start \
|
|
219
|
+
--env production \
|
|
220
|
+
--max-connections 500 \
|
|
221
|
+
--query-timeout 60000
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
### Docker Deployment
|
|
225
|
+
|
|
226
|
+
Create a `docker-compose.yml`:
|
|
227
|
+
|
|
228
|
+
```yaml
|
|
229
|
+
version: '3.8'
|
|
230
|
+
services:
|
|
231
|
+
graphql-server:
|
|
232
|
+
image: node:22-alpine
|
|
233
|
+
working_dir: /app
|
|
234
|
+
command: npx @0xobelisk/graphql-server start
|
|
235
|
+
ports:
|
|
236
|
+
- '4000:4000'
|
|
237
|
+
environment:
|
|
238
|
+
- DATABASE_URL=postgres://user:password@postgres:5432/sui_indexer
|
|
239
|
+
- PORT=4000
|
|
240
|
+
- ENABLE_SUBSCRIPTIONS=true
|
|
241
|
+
- NODE_ENV=production
|
|
242
|
+
depends_on:
|
|
243
|
+
- postgres
|
|
244
|
+
|
|
245
|
+
postgres:
|
|
246
|
+
image: postgres:15
|
|
247
|
+
environment:
|
|
248
|
+
- POSTGRES_DB=sui_indexer
|
|
249
|
+
- POSTGRES_USER=user
|
|
250
|
+
- POSTGRES_PASSWORD=password
|
|
251
|
+
volumes:
|
|
252
|
+
- postgres_data:/var/lib/postgresql/data
|
|
253
|
+
|
|
254
|
+
volumes:
|
|
255
|
+
postgres_data:
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
### Production Configuration
|
|
259
|
+
|
|
260
|
+
```bash
|
|
261
|
+
# CLI approach for production
|
|
262
|
+
dubhe-graphql-server start \
|
|
263
|
+
--env production \
|
|
264
|
+
--database-url "postgres://user:password@prod-db:5432/sui_indexer" \
|
|
265
|
+
--port 4000 \
|
|
266
|
+
--no-cors \
|
|
267
|
+
--max-connections 500 \
|
|
268
|
+
--query-timeout 60000 \
|
|
269
|
+
--enable-metrics
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
## Configuration
|
|
273
|
+
|
|
274
|
+
### Server Configuration Interface
|
|
275
|
+
|
|
276
|
+
The server uses a comprehensive configuration interface:
|
|
277
|
+
|
|
278
|
+
```typescript
|
|
279
|
+
interface ServerConfig {
|
|
280
|
+
// Basic server configuration
|
|
281
|
+
port: string;
|
|
282
|
+
databaseUrl: string;
|
|
283
|
+
schema: string;
|
|
284
|
+
endpoint: string;
|
|
285
|
+
cors: boolean;
|
|
286
|
+
subscriptions: boolean;
|
|
287
|
+
env: string;
|
|
288
|
+
|
|
289
|
+
// Debug configuration
|
|
290
|
+
debug: boolean;
|
|
291
|
+
|
|
292
|
+
// Performance configuration
|
|
293
|
+
queryTimeout: number;
|
|
294
|
+
maxConnections: number;
|
|
295
|
+
heartbeatInterval: number;
|
|
296
|
+
enableMetrics: boolean;
|
|
297
|
+
|
|
298
|
+
// Subscription capabilities
|
|
299
|
+
enableLiveQueries: boolean;
|
|
300
|
+
enablePgSubscriptions: boolean;
|
|
301
|
+
enableNativeWebSocket: boolean;
|
|
302
|
+
realtimePort?: number;
|
|
303
|
+
|
|
304
|
+
// Internal debug flags
|
|
305
|
+
debugNotifications: boolean;
|
|
306
|
+
}
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
### Database Permissions
|
|
310
|
+
|
|
311
|
+
Set up proper database permissions:
|
|
312
|
+
|
|
313
|
+
```sql
|
|
314
|
+
-- Create read-only user
|
|
315
|
+
CREATE USER graphql_readonly WITH PASSWORD 'secure_password';
|
|
316
|
+
|
|
317
|
+
-- Grant query permissions
|
|
318
|
+
GRANT CONNECT ON DATABASE sui_indexer TO graphql_readonly;
|
|
319
|
+
GRANT USAGE ON SCHEMA public TO graphql_readonly;
|
|
320
|
+
GRANT SELECT ON ALL TABLES IN SCHEMA public TO graphql_readonly;
|
|
321
|
+
|
|
322
|
+
-- If write permissions needed
|
|
323
|
+
GRANT INSERT, UPDATE, DELETE ON specific_tables TO graphql_readonly;
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
## Monitoring and Debugging
|
|
327
|
+
|
|
328
|
+
### Health Checks
|
|
329
|
+
|
|
330
|
+
The server provides comprehensive monitoring endpoints:
|
|
331
|
+
|
|
332
|
+
- `http://localhost:4000/` - Welcome page with system information
|
|
333
|
+
- `http://localhost:4000/health` - Health check endpoint with subscription status
|
|
334
|
+
- `http://localhost:4000/subscription-config` - Client configuration for subscriptions
|
|
335
|
+
- `http://localhost:4000/subscription-docs` - Configuration documentation
|
|
336
|
+
- `http://localhost:4000/playground` - Enhanced GraphQL Playground
|
|
337
|
+
|
|
338
|
+
### Debug Mode
|
|
339
|
+
|
|
340
|
+
Enable debug mode for detailed logging:
|
|
341
|
+
|
|
342
|
+
```bash
|
|
343
|
+
# Enable debug mode with verbose logging
|
|
344
|
+
dubhe-graphql-server start --debug
|
|
345
|
+
|
|
346
|
+
# Enable performance metrics
|
|
347
|
+
dubhe-graphql-server start --enable-metrics
|
|
348
|
+
|
|
349
|
+
# Combine both for comprehensive monitoring
|
|
350
|
+
dubhe-graphql-server start --debug --enable-metrics
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
### Performance Monitoring
|
|
354
|
+
|
|
355
|
+
The server includes built-in performance monitoring:
|
|
356
|
+
|
|
357
|
+
- **Query Logging**: SQL query logs (controlled by `--debug`)
|
|
358
|
+
- **Request Metrics**: HTTP request timing and status
|
|
359
|
+
- **Connection Monitoring**: Database connection pool status
|
|
360
|
+
- **WebSocket Metrics**: Subscription connection statistics
|
|
361
|
+
|
|
362
|
+
## Troubleshooting
|
|
363
|
+
|
|
364
|
+
### Common Issues
|
|
365
|
+
|
|
366
|
+
1. **Database Connection Failed**
|
|
367
|
+
|
|
368
|
+
```
|
|
369
|
+
Solution: Check DATABASE_URL and database service status
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
2. **Table Scan Empty**
|
|
373
|
+
|
|
374
|
+
```
|
|
375
|
+
Solution: Ensure dubhe-indexer is running and has created tables
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
3. **Schema Generation Failed**
|
|
379
|
+
|
|
380
|
+
```
|
|
381
|
+
Solution: Check if table_fields table exists and has data
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
4. **WebSocket Connection Failed**
|
|
385
|
+
```
|
|
386
|
+
Solution: Check firewall settings and ENABLE_SUBSCRIPTIONS configuration
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
### Debug Commands
|
|
390
|
+
|
|
391
|
+
```bash
|
|
392
|
+
# View generated schema
|
|
393
|
+
ls -la *.graphql
|
|
394
|
+
|
|
395
|
+
# Check database connection
|
|
396
|
+
psql $DATABASE_URL -c "SELECT version();"
|
|
397
|
+
|
|
398
|
+
# Test GraphQL endpoint
|
|
399
|
+
curl -X POST http://localhost:4000/graphql \
|
|
400
|
+
-H "Content-Type: application/json" \
|
|
401
|
+
-d '{"query": "{ __schema { types { name } } }"}'
|
|
402
|
+
```
|
|
403
|
+
|
|
404
|
+
## Architecture
|
|
405
|
+
|
|
406
|
+
### System Architecture
|
|
407
|
+
|
|
408
|
+
```
|
|
409
|
+
dubhe-indexer database
|
|
410
|
+
↓
|
|
411
|
+
[Database Introspector] ← Scans table structures
|
|
412
|
+
↓
|
|
413
|
+
[PostGraphile] ← Generates GraphQL schema
|
|
414
|
+
↓
|
|
415
|
+
[Enhanced Server Manager] ← Manages HTTP/WebSocket
|
|
416
|
+
↓
|
|
417
|
+
[GraphQL API + WebSocket] ← Unified endpoint
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
### Core Components
|
|
421
|
+
|
|
422
|
+
1. **Database Introspector**:
|
|
423
|
+
|
|
424
|
+
- Scans `store_*` tables and system tables
|
|
425
|
+
- Reads field metadata from `table_fields`
|
|
426
|
+
- Monitors database connection health
|
|
427
|
+
|
|
428
|
+
2. **PostGraphile Engine**:
|
|
429
|
+
|
|
430
|
+
- Generates GraphQL schema from database structure
|
|
431
|
+
- Provides CRUD operations and filtering
|
|
432
|
+
- Handles connection pooling and query optimization
|
|
433
|
+
|
|
434
|
+
3. **Subscription Manager**:
|
|
435
|
+
|
|
436
|
+
- Manages PostgreSQL LISTEN/NOTIFY subscriptions
|
|
437
|
+
- Handles WebSocket connections and heartbeat
|
|
438
|
+
- Provides universal subscriptions for all store tables
|
|
439
|
+
|
|
440
|
+
4. **Enhanced Server Manager**:
|
|
441
|
+
- Express.js server with modular middleware
|
|
442
|
+
- Welcome page, health checks, and documentation
|
|
443
|
+
- GraphQL Playground integration
|
|
444
|
+
|
|
445
|
+
### Supported Table Types
|
|
446
|
+
|
|
447
|
+
1. **System Tables**: Auto-detected indexer tables
|
|
448
|
+
|
|
449
|
+
- `table_fields` - Table structure metadata (stores field definitions for dynamic tables)
|
|
450
|
+
|
|
451
|
+
2. **Dynamic Tables**: Contract-defined tables
|
|
452
|
+
- `store_*` - Tables created from your `dubhe.config.json` (e.g., `store_component0`, `store_component1`)
|
|
453
|
+
- Each table includes system fields: `created_at_timestamp_ms`, `updated_at_timestamp_ms`, `is_deleted`
|
|
454
|
+
- Automatically generate GraphQL types and operations
|
|
455
|
+
|
|
456
|
+
## Best Practices
|
|
457
|
+
|
|
458
|
+
### Development
|
|
459
|
+
|
|
460
|
+
1. **Use debug mode** for development: `--debug --enable-metrics`
|
|
461
|
+
2. **Monitor welcome page** for table discovery status
|
|
462
|
+
3. **Use GraphQL Playground** for query development and testing
|
|
463
|
+
4. **Check health endpoint** regularly for system status
|
|
464
|
+
|
|
465
|
+
### Production
|
|
466
|
+
|
|
467
|
+
1. **Configure connection pooling**: Use `--max-connections` appropriately
|
|
468
|
+
2. **Set proper timeouts**: Configure `--query-timeout` based on usage
|
|
469
|
+
3. **Enable security**: Use `--no-cors` or configure specific origins
|
|
470
|
+
4. **Monitor performance**: Enable `--enable-metrics` for production monitoring
|
|
471
|
+
5. **Use read-only database user** for security
|
|
472
|
+
6. **Implement rate limiting** at the reverse proxy level
|
|
473
|
+
7. **Set up proper logging** and monitoring infrastructure
|
|
474
|
+
|
|
475
|
+
### Database Optimization
|
|
476
|
+
|
|
477
|
+
1. **Create indexes** on frequently queried columns
|
|
478
|
+
2. **Use connection pooling** efficiently
|
|
479
|
+
3. **Monitor subscription connections** to prevent resource exhaustion
|
|
480
|
+
4. **Configure PostgreSQL** for optimal performance with LISTEN/NOTIFY
|
|
481
|
+
|
|
482
|
+
### Integration
|
|
483
|
+
|
|
484
|
+
1. **Start dubhe-indexer first** before GraphQL server
|
|
485
|
+
2. **Ensure database schema compatibility** between services
|
|
486
|
+
3. **Use environment-specific configurations** for different stages
|
|
487
|
+
4. **Implement proper error handling** in client applications
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
4
|
+
if (k2 === undefined) k2 = k;
|
|
5
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
6
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
7
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
8
|
+
}
|
|
9
|
+
Object.defineProperty(o, k2, desc);
|
|
10
|
+
}) : (function(o, m, k, k2) {
|
|
11
|
+
if (k2 === undefined) k2 = k;
|
|
12
|
+
o[k2] = m[k];
|
|
13
|
+
}));
|
|
14
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
15
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
16
|
+
}) : function(o, v) {
|
|
17
|
+
o["default"] = v;
|
|
18
|
+
});
|
|
19
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
20
|
+
var ownKeys = function(o) {
|
|
21
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
22
|
+
var ar = [];
|
|
23
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
24
|
+
return ar;
|
|
25
|
+
};
|
|
26
|
+
return ownKeys(o);
|
|
27
|
+
};
|
|
28
|
+
return function (mod) {
|
|
29
|
+
if (mod && mod.__esModule) return mod;
|
|
30
|
+
var result = {};
|
|
31
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
32
|
+
__setModuleDefault(result, mod);
|
|
33
|
+
return result;
|
|
34
|
+
};
|
|
35
|
+
})();
|
|
36
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
37
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
38
|
+
};
|
|
39
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
40
|
+
const yargs_1 = __importDefault(require("yargs"));
|
|
41
|
+
const helpers_1 = require("yargs/helpers");
|
|
42
|
+
const dotenv = __importStar(require("dotenv"));
|
|
43
|
+
const server_1 = require("./server");
|
|
44
|
+
const logger_1 = require("./utils/logger");
|
|
45
|
+
const package_json_1 = __importDefault(require("../package.json"));
|
|
46
|
+
// Load environment variables
|
|
47
|
+
dotenv.config();
|
|
48
|
+
// Helper function to get environment variable or default value
|
|
49
|
+
const getEnvOrDefault = (envKey, defaultValue) => {
|
|
50
|
+
return process.env[envKey] || defaultValue;
|
|
51
|
+
};
|
|
52
|
+
const getBooleanEnvOrDefault = (envKey, defaultValue) => {
|
|
53
|
+
const envValue = process.env[envKey];
|
|
54
|
+
if (envValue === undefined)
|
|
55
|
+
return defaultValue;
|
|
56
|
+
return envValue.toLowerCase() === 'true';
|
|
57
|
+
};
|
|
58
|
+
const cli = (0, yargs_1.default)((0, helpers_1.hideBin)(process.argv))
|
|
59
|
+
.scriptName('dubhe-graphql-server')
|
|
60
|
+
.usage('$0 <command> [options]')
|
|
61
|
+
.version(package_json_1.default.version)
|
|
62
|
+
.help('help')
|
|
63
|
+
.alias('h', 'help')
|
|
64
|
+
.alias('v', 'version')
|
|
65
|
+
.demandCommand(1, 'You need to specify a command')
|
|
66
|
+
.recommendCommands()
|
|
67
|
+
.strict();
|
|
68
|
+
// Start command
|
|
69
|
+
cli.command('start', 'Start GraphQL server', (yargs) => {
|
|
70
|
+
return (yargs
|
|
71
|
+
.option('port', {
|
|
72
|
+
alias: 'p',
|
|
73
|
+
type: 'string',
|
|
74
|
+
default: getEnvOrDefault('PORT', '4000'),
|
|
75
|
+
describe: 'Server port (env: PORT)'
|
|
76
|
+
})
|
|
77
|
+
.option('database-url', {
|
|
78
|
+
alias: 'd',
|
|
79
|
+
type: 'string',
|
|
80
|
+
default: getEnvOrDefault('DATABASE_URL', 'postgres://postgres:postgres@127.0.0.1:5432/postgres'),
|
|
81
|
+
describe: 'Database connection URL (env: DATABASE_URL)'
|
|
82
|
+
})
|
|
83
|
+
.option('schema', {
|
|
84
|
+
alias: 's',
|
|
85
|
+
type: 'string',
|
|
86
|
+
default: getEnvOrDefault('PG_SCHEMA', 'public'),
|
|
87
|
+
describe: 'PostgreSQL schema name (env: PG_SCHEMA)'
|
|
88
|
+
})
|
|
89
|
+
.option('endpoint', {
|
|
90
|
+
alias: 'e',
|
|
91
|
+
type: 'string',
|
|
92
|
+
default: getEnvOrDefault('GRAPHQL_ENDPOINT', '/graphql'),
|
|
93
|
+
describe: 'GraphQL endpoint path (env: GRAPHQL_ENDPOINT)'
|
|
94
|
+
})
|
|
95
|
+
.option('cors', {
|
|
96
|
+
type: 'boolean',
|
|
97
|
+
default: getBooleanEnvOrDefault('ENABLE_CORS', true),
|
|
98
|
+
describe: 'Enable CORS (env: ENABLE_CORS)'
|
|
99
|
+
})
|
|
100
|
+
.option('subscriptions', {
|
|
101
|
+
type: 'boolean',
|
|
102
|
+
default: getBooleanEnvOrDefault('ENABLE_SUBSCRIPTIONS', true),
|
|
103
|
+
describe: 'Enable GraphQL subscriptions (env: ENABLE_SUBSCRIPTIONS)'
|
|
104
|
+
})
|
|
105
|
+
.option('env', {
|
|
106
|
+
type: 'string',
|
|
107
|
+
default: getEnvOrDefault('NODE_ENV', 'development'),
|
|
108
|
+
choices: ['development', 'production'],
|
|
109
|
+
describe: 'Environment mode (env: NODE_ENV)'
|
|
110
|
+
})
|
|
111
|
+
// Debug configuration
|
|
112
|
+
.option('debug', {
|
|
113
|
+
type: 'boolean',
|
|
114
|
+
default: getBooleanEnvOrDefault('DEBUG', false),
|
|
115
|
+
describe: 'Enable debug mode (verbose logging + query logs) (env: DEBUG)'
|
|
116
|
+
})
|
|
117
|
+
// Performance configuration
|
|
118
|
+
.option('query-timeout', {
|
|
119
|
+
type: 'number',
|
|
120
|
+
default: parseInt(getEnvOrDefault('QUERY_TIMEOUT', '30000')),
|
|
121
|
+
describe: 'GraphQL query timeout in milliseconds (env: QUERY_TIMEOUT)'
|
|
122
|
+
})
|
|
123
|
+
.option('max-connections', {
|
|
124
|
+
type: 'number',
|
|
125
|
+
default: parseInt(getEnvOrDefault('MAX_CONNECTIONS', '1000')),
|
|
126
|
+
describe: 'Maximum database connections (env: MAX_CONNECTIONS)'
|
|
127
|
+
})
|
|
128
|
+
.option('heartbeat-interval', {
|
|
129
|
+
type: 'number',
|
|
130
|
+
default: parseInt(getEnvOrDefault('HEARTBEAT_INTERVAL', '30000')),
|
|
131
|
+
describe: 'WebSocket heartbeat interval in milliseconds (env: HEARTBEAT_INTERVAL)'
|
|
132
|
+
})
|
|
133
|
+
.option('enable-metrics', {
|
|
134
|
+
type: 'boolean',
|
|
135
|
+
default: getBooleanEnvOrDefault('ENABLE_METRICS', false),
|
|
136
|
+
describe: 'Enable performance metrics (env: ENABLE_METRICS)'
|
|
137
|
+
})
|
|
138
|
+
// Subscription capabilities
|
|
139
|
+
.option('enable-live-queries', {
|
|
140
|
+
type: 'boolean',
|
|
141
|
+
default: getBooleanEnvOrDefault('ENABLE_LIVE_QUERIES', true),
|
|
142
|
+
describe: 'Enable GraphQL live queries (env: ENABLE_LIVE_QUERIES)'
|
|
143
|
+
})
|
|
144
|
+
.option('enable-pg-subscriptions', {
|
|
145
|
+
type: 'boolean',
|
|
146
|
+
default: getBooleanEnvOrDefault('ENABLE_PG_SUBSCRIPTIONS', true),
|
|
147
|
+
describe: 'Enable PostgreSQL subscriptions (env: ENABLE_PG_SUBSCRIPTIONS)'
|
|
148
|
+
})
|
|
149
|
+
.option('enable-native-websocket', {
|
|
150
|
+
type: 'boolean',
|
|
151
|
+
default: getBooleanEnvOrDefault('ENABLE_NATIVE_WEBSOCKET', true),
|
|
152
|
+
describe: 'Enable native WebSocket support (env: ENABLE_NATIVE_WEBSOCKET)'
|
|
153
|
+
})
|
|
154
|
+
.option('realtime-port', {
|
|
155
|
+
type: 'number',
|
|
156
|
+
default: process.env.REALTIME_PORT ? parseInt(process.env.REALTIME_PORT) : undefined,
|
|
157
|
+
describe: 'Realtime WebSocket port (env: REALTIME_PORT)'
|
|
158
|
+
})
|
|
159
|
+
.example('$0 start -p 4000 -d postgres://user:pass@localhost/db', 'Start server with custom port and database')
|
|
160
|
+
.example('$0 start --no-cors --no-subscriptions', 'Start server with CORS and subscriptions disabled')
|
|
161
|
+
.example('$0 start --debug', 'Start server in debug mode (verbose logging + notifications)')
|
|
162
|
+
.example('$0 start --debug --enable-metrics', 'Start server in debug mode with performance metrics'));
|
|
163
|
+
}, async (argv) => {
|
|
164
|
+
try {
|
|
165
|
+
logger_1.systemLogger.info('🚀 Starting Dubhe GraphQL server via CLI...', {
|
|
166
|
+
port: argv.port,
|
|
167
|
+
schema: argv.schema,
|
|
168
|
+
endpoint: argv.endpoint,
|
|
169
|
+
environment: argv.env
|
|
170
|
+
});
|
|
171
|
+
// Build server configuration object
|
|
172
|
+
const serverConfig = {
|
|
173
|
+
// Basic server configuration
|
|
174
|
+
port: argv.port,
|
|
175
|
+
databaseUrl: argv['database-url'],
|
|
176
|
+
schema: argv.schema,
|
|
177
|
+
endpoint: argv.endpoint,
|
|
178
|
+
cors: argv.cors,
|
|
179
|
+
subscriptions: argv.subscriptions,
|
|
180
|
+
env: argv.env,
|
|
181
|
+
// Debug configuration (explicit control)
|
|
182
|
+
debug: argv.debug,
|
|
183
|
+
// Performance configuration
|
|
184
|
+
queryTimeout: argv['query-timeout'],
|
|
185
|
+
maxConnections: argv['max-connections'],
|
|
186
|
+
heartbeatInterval: argv['heartbeat-interval'],
|
|
187
|
+
// Subscription capabilities
|
|
188
|
+
enableLiveQueries: argv['enable-live-queries'],
|
|
189
|
+
enablePgSubscriptions: argv['enable-pg-subscriptions'],
|
|
190
|
+
enableNativeWebSocket: argv['enable-native-websocket'],
|
|
191
|
+
realtimePort: argv['realtime-port'],
|
|
192
|
+
// Debug configuration (notifications only in development or when debug is enabled)
|
|
193
|
+
debugNotifications: argv.debug || argv.env === 'development',
|
|
194
|
+
enableMetrics: argv['enable-metrics']
|
|
195
|
+
};
|
|
196
|
+
// Pass configuration object directly to startServer
|
|
197
|
+
await (0, server_1.startServer)(serverConfig);
|
|
198
|
+
}
|
|
199
|
+
catch (error) {
|
|
200
|
+
logger_1.systemLogger.error('Failed to start server via CLI', error);
|
|
201
|
+
process.exit(1);
|
|
202
|
+
}
|
|
203
|
+
});
|
|
204
|
+
// Parse command line arguments
|
|
205
|
+
cli.parse();
|
|
206
|
+
//# sourceMappingURL=cli.js.map
|