@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.
- package/Dockerfile +31 -0
- package/EXPRESS_MIGRATION.md +176 -0
- package/LICENSE +92 -0
- package/README.md +908 -0
- package/dist/config/subscription-config.d.ts +47 -0
- package/dist/config/subscription-config.d.ts.map +1 -0
- package/dist/config/subscription-config.js +133 -0
- package/dist/config/subscription-config.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +217 -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 +97 -0
- package/dist/plugins/enhanced-playground.js.map +1 -0
- package/dist/plugins/enhanced-server-manager.d.ts +28 -0
- package/dist/plugins/enhanced-server-manager.d.ts.map +1 -0
- package/dist/plugins/enhanced-server-manager.js +232 -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 +183 -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/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 +232 -0
- package/dist/utils/logger/index.js.map +1 -0
- package/docker-compose.yml +87 -0
- package/package.json +71 -0
- package/server.log +62 -0
- package/src/config/subscription-config.ts +186 -0
- package/src/index.ts +239 -0
- package/src/plugins/README.md +123 -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 +105 -0
- package/src/plugins/enhanced-server-manager.ts +282 -0
- package/src/plugins/index.ts +9 -0
- package/src/plugins/postgraphile-config.ts +226 -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/universal-subscriptions.ts +397 -0
- package/src/utils/logger/README.md +193 -0
- package/src/utils/logger/index.ts +315 -0
- package/sui-indexer-schema.graphql +1004 -0
- package/test-express.js +124 -0
- package/test_listen_subscription.js +121 -0
- package/test_notification.js +63 -0
- package/tsconfig.json +28 -0
package/README.md
ADDED
|
@@ -0,0 +1,908 @@
|
|
|
1
|
+
# 🚀 Universal GraphQL Server
|
|
2
|
+
|
|
3
|
+
一个智能的 GraphQL 服务器适配器,能够自动连接到 `sui-rust-indexer` 创建的数据库,并动态生成完整的 GraphQL API。
|
|
4
|
+
|
|
5
|
+
## ✨ 核心特性
|
|
6
|
+
|
|
7
|
+
### 🎯 智能数据库适配
|
|
8
|
+
- **动态扫描**: 自动扫描 `sui-rust-indexer` 创建的所有表结构
|
|
9
|
+
- **PostGraphile 驱动**: 基于强大的 PostGraphile 自动生成 GraphQL API
|
|
10
|
+
- **零配置**: 无需手动定义 schema,基于现有数据库自动推断
|
|
11
|
+
|
|
12
|
+
### 🔍 高级过滤功能
|
|
13
|
+
- **丰富的操作符**: 支持等于、大于、小于、包含、模糊匹配等20+种过滤操作符
|
|
14
|
+
- **逻辑组合**: 支持AND、OR、NOT逻辑操作符进行复杂条件组合
|
|
15
|
+
- **全字段过滤**: 自动为所有字段生成相应的过滤器
|
|
16
|
+
- **类型智能**: 根据字段类型自动提供合适的过滤操作符
|
|
17
|
+
- **关系过滤**: 支持基于关联表字段进行过滤
|
|
18
|
+
|
|
19
|
+
### 📈 增强的排序和分页
|
|
20
|
+
- **全字段排序**: 支持对任意字段进行升序/降序排序
|
|
21
|
+
- **多字段排序**: 支持同时按多个字段排序
|
|
22
|
+
- **高效分页**: Relay风格的cursor分页和offset分页
|
|
23
|
+
- **性能优化**: 智能查询优化和索引建议
|
|
24
|
+
|
|
25
|
+
### 📡 实时功能
|
|
26
|
+
- **WebSocket 支持**: 完整的 GraphQL 订阅功能
|
|
27
|
+
- **实时查询**: PostGraphile Live Queries 支持
|
|
28
|
+
- **数据监听**: 可选的数据库变更监听
|
|
29
|
+
|
|
30
|
+
### 🛠️ 开发体验
|
|
31
|
+
- **GraphiQL**: 内置的 GraphQL 查询界面
|
|
32
|
+
- **自动文档**: 基于数据库结构自动生成的 API 文档
|
|
33
|
+
- **类型安全**: 完整的 TypeScript 支持
|
|
34
|
+
- **美观界面**: 现代化的欢迎页面和信息展示
|
|
35
|
+
|
|
36
|
+
## 📦 安装
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
# 进入项目目录
|
|
40
|
+
cd packages/universal-graphql-server
|
|
41
|
+
|
|
42
|
+
# 安装依赖
|
|
43
|
+
pnpm install
|
|
44
|
+
|
|
45
|
+
# 或使用 npm
|
|
46
|
+
npm install
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## 🔧 配置
|
|
50
|
+
|
|
51
|
+
### 环境变量
|
|
52
|
+
|
|
53
|
+
创建 `.env` 文件:
|
|
54
|
+
|
|
55
|
+
```env
|
|
56
|
+
# 数据库配置(连接到 sui-rust-indexer 的数据库)
|
|
57
|
+
DATABASE_URL=postgres://username:password@localhost:5432/sui_indexer_db
|
|
58
|
+
|
|
59
|
+
# 服务器配置
|
|
60
|
+
PORT=4000
|
|
61
|
+
NODE_ENV=development
|
|
62
|
+
|
|
63
|
+
# GraphQL 配置
|
|
64
|
+
GRAPHQL_ENDPOINT=/graphql
|
|
65
|
+
PG_SCHEMA=public
|
|
66
|
+
|
|
67
|
+
# 功能开关
|
|
68
|
+
ENABLE_CORS=true
|
|
69
|
+
ENABLE_SUBSCRIPTIONS=true
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### 前置条件
|
|
73
|
+
|
|
74
|
+
确保 `sui-rust-indexer` 已经运行并创建了数据库表:
|
|
75
|
+
|
|
76
|
+
1. **系统表**: `__dubheStoreTransactions`, `__dubheStoreSchemas`, `__dubheStoreEvents`
|
|
77
|
+
2. **元数据表**: `table_fields` (存储动态表结构信息)
|
|
78
|
+
3. **动态表**: `store_*` 表(根据配置文件动态创建)
|
|
79
|
+
|
|
80
|
+
## 🚀 运行
|
|
81
|
+
|
|
82
|
+
### 开发模式
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
# 启动开发服务器(支持热重载)
|
|
86
|
+
pnpm dev
|
|
87
|
+
|
|
88
|
+
# 或使用 npm
|
|
89
|
+
npm run dev
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### 生产模式
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
# 构建项目
|
|
96
|
+
pnpm build
|
|
97
|
+
|
|
98
|
+
# 启动生产服务器
|
|
99
|
+
pnpm start
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## 📊 访问端点
|
|
103
|
+
|
|
104
|
+
启动服务器后,你可以访问:
|
|
105
|
+
|
|
106
|
+
- **欢迎页面**: `http://localhost:4000` - 查看扫描到的表和系统信息
|
|
107
|
+
- **GraphQL API**: `http://localhost:4000/graphql` - API 端点
|
|
108
|
+
- **GraphiQL**: `http://localhost:4000/graphiql` - 交互式查询界面
|
|
109
|
+
- **WebSocket**: `ws://localhost:4000/graphql` - 订阅功能
|
|
110
|
+
|
|
111
|
+
## 🎮 使用示例
|
|
112
|
+
|
|
113
|
+
### 查询系统表
|
|
114
|
+
|
|
115
|
+
```graphql
|
|
116
|
+
# 查询 Schemas 表
|
|
117
|
+
query GetSchemas {
|
|
118
|
+
allDubheStoreSchemas(first: 10) {
|
|
119
|
+
nodes {
|
|
120
|
+
id
|
|
121
|
+
name
|
|
122
|
+
key1
|
|
123
|
+
key2
|
|
124
|
+
value
|
|
125
|
+
lastUpdateCheckpoint
|
|
126
|
+
isRemoved
|
|
127
|
+
createdAt
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
# 查询 Transactions 表
|
|
133
|
+
query GetTransactions {
|
|
134
|
+
allDubheStoreTransactions(first: 10) {
|
|
135
|
+
nodes {
|
|
136
|
+
id
|
|
137
|
+
sender
|
|
138
|
+
checkpoint
|
|
139
|
+
digest
|
|
140
|
+
package
|
|
141
|
+
module
|
|
142
|
+
function
|
|
143
|
+
arguments
|
|
144
|
+
createdAt
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
# 查询 Events 表
|
|
150
|
+
query GetEvents {
|
|
151
|
+
allDubheStoreEvents(first: 10) {
|
|
152
|
+
nodes {
|
|
153
|
+
id
|
|
154
|
+
sender
|
|
155
|
+
name
|
|
156
|
+
value
|
|
157
|
+
checkpoint
|
|
158
|
+
digest
|
|
159
|
+
createdAt
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### 查询动态表
|
|
166
|
+
|
|
167
|
+
如果 `sui-rust-indexer` 创建了动态表(例如从 `config.json` 配置),你可以查询它们:
|
|
168
|
+
|
|
169
|
+
```graphql
|
|
170
|
+
# 查询 store_accounts 表(如果存在)
|
|
171
|
+
query GetAccounts {
|
|
172
|
+
allStoreAccounts {
|
|
173
|
+
nodes {
|
|
174
|
+
assetId
|
|
175
|
+
account
|
|
176
|
+
balance
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
# 查询 store_position 表(如果存在)
|
|
182
|
+
query GetPositions {
|
|
183
|
+
allStorePositions {
|
|
184
|
+
nodes {
|
|
185
|
+
player
|
|
186
|
+
x
|
|
187
|
+
y
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
### 实时订阅
|
|
194
|
+
|
|
195
|
+
```graphql
|
|
196
|
+
# 订阅 Schemas 变更
|
|
197
|
+
subscription OnSchemaChanges {
|
|
198
|
+
allDubheStoreSchemas(first: 1, orderBy: [CREATED_AT_DESC]) {
|
|
199
|
+
nodes {
|
|
200
|
+
id
|
|
201
|
+
name
|
|
202
|
+
value
|
|
203
|
+
createdAt
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
# 订阅 Events
|
|
209
|
+
subscription OnNewEvents {
|
|
210
|
+
allDubheStoreEvents(first: 1, orderBy: [CREATED_AT_DESC]) {
|
|
211
|
+
nodes {
|
|
212
|
+
id
|
|
213
|
+
name
|
|
214
|
+
value
|
|
215
|
+
checkpoint
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
### 高级查询
|
|
222
|
+
|
|
223
|
+
```graphql
|
|
224
|
+
# 分页查询
|
|
225
|
+
query GetSchemasPaginated($after: Cursor) {
|
|
226
|
+
allDubheStoreSchemas(first: 10, after: $after) {
|
|
227
|
+
pageInfo {
|
|
228
|
+
hasNextPage
|
|
229
|
+
endCursor
|
|
230
|
+
}
|
|
231
|
+
nodes {
|
|
232
|
+
id
|
|
233
|
+
name
|
|
234
|
+
value
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
# 条件过滤
|
|
240
|
+
query GetSchemasByName($name: String!) {
|
|
241
|
+
allDubheStoreSchemas(condition: { name: $name }) {
|
|
242
|
+
nodes {
|
|
243
|
+
id
|
|
244
|
+
name
|
|
245
|
+
key1
|
|
246
|
+
key2
|
|
247
|
+
value
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
# 排序查询
|
|
253
|
+
query GetRecentTransactions {
|
|
254
|
+
allDubheStoreTransactions(
|
|
255
|
+
first: 20,
|
|
256
|
+
orderBy: [CREATED_AT_DESC]
|
|
257
|
+
) {
|
|
258
|
+
nodes {
|
|
259
|
+
id
|
|
260
|
+
sender
|
|
261
|
+
function
|
|
262
|
+
checkpoint
|
|
263
|
+
createdAt
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
### 高级过滤查询
|
|
270
|
+
|
|
271
|
+
现在支持强大的过滤功能,包括多种操作符和逻辑组合:
|
|
272
|
+
|
|
273
|
+
```graphql
|
|
274
|
+
# 基础过滤 - 使用大于操作符
|
|
275
|
+
query GetHighValueAccounts {
|
|
276
|
+
storeAccounts(filter: {
|
|
277
|
+
balance: { gt: "1000" }
|
|
278
|
+
}) {
|
|
279
|
+
nodes {
|
|
280
|
+
assetId
|
|
281
|
+
account
|
|
282
|
+
balance
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
# 多条件过滤 - 隐式AND组合
|
|
288
|
+
query GetSpecificAccounts {
|
|
289
|
+
storeAccounts(filter: {
|
|
290
|
+
balance: { gte: "100", lte: "10000" },
|
|
291
|
+
assetId: { startsWith: "0x2" }
|
|
292
|
+
}) {
|
|
293
|
+
nodes {
|
|
294
|
+
assetId
|
|
295
|
+
account
|
|
296
|
+
balance
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
# 逻辑操作符 - OR组合
|
|
302
|
+
query GetAccountsWithConditions {
|
|
303
|
+
storeAccounts(filter: {
|
|
304
|
+
or: [
|
|
305
|
+
{ balance: { gt: "50000" } },
|
|
306
|
+
{ assetId: { in: ["0x123", "0x456", "0x789"] } }
|
|
307
|
+
]
|
|
308
|
+
}) {
|
|
309
|
+
nodes {
|
|
310
|
+
assetId
|
|
311
|
+
account
|
|
312
|
+
balance
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
# 复杂逻辑组合 - AND, OR, NOT
|
|
318
|
+
query GetComplexFilteredAccounts {
|
|
319
|
+
storeAccounts(filter: {
|
|
320
|
+
and: [
|
|
321
|
+
{
|
|
322
|
+
or: [
|
|
323
|
+
{ balance: { gt: "1000" } },
|
|
324
|
+
{ assetId: { like: "%special%" } }
|
|
325
|
+
]
|
|
326
|
+
},
|
|
327
|
+
{
|
|
328
|
+
not: {
|
|
329
|
+
account: { includesInsensitive: "test" }
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
]
|
|
333
|
+
}) {
|
|
334
|
+
nodes {
|
|
335
|
+
assetId
|
|
336
|
+
account
|
|
337
|
+
balance
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
# 字符串模糊搜索
|
|
343
|
+
query SearchPlayers {
|
|
344
|
+
storeEncounters(filter: {
|
|
345
|
+
player: { includesInsensitive: "alice" },
|
|
346
|
+
monster: { isNull: false }
|
|
347
|
+
}) {
|
|
348
|
+
nodes {
|
|
349
|
+
player
|
|
350
|
+
monster
|
|
351
|
+
catchAttempts
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
# 数组和范围查询
|
|
357
|
+
query GetPositionsInRange {
|
|
358
|
+
storePositions(filter: {
|
|
359
|
+
player: { in: ["player1", "player2", "player3"] },
|
|
360
|
+
x: { gte: "10", lte: "100" },
|
|
361
|
+
y: { isNull: false }
|
|
362
|
+
}) {
|
|
363
|
+
nodes {
|
|
364
|
+
player
|
|
365
|
+
x
|
|
366
|
+
y
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
### 增强的排序功能
|
|
373
|
+
|
|
374
|
+
支持所有字段的多种排序组合:
|
|
375
|
+
|
|
376
|
+
```graphql
|
|
377
|
+
# 单字段排序
|
|
378
|
+
query GetAccountsByBalance {
|
|
379
|
+
storeAccounts(
|
|
380
|
+
orderBy: [BALANCE_DESC]
|
|
381
|
+
) {
|
|
382
|
+
nodes {
|
|
383
|
+
assetId
|
|
384
|
+
account
|
|
385
|
+
balance
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
# 多字段排序
|
|
391
|
+
query GetAccountsMultiSort {
|
|
392
|
+
storeAccounts(
|
|
393
|
+
orderBy: [ASSET_ID_ASC, BALANCE_DESC]
|
|
394
|
+
) {
|
|
395
|
+
nodes {
|
|
396
|
+
assetId
|
|
397
|
+
account
|
|
398
|
+
balance
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
# 过滤 + 排序 + 分页
|
|
404
|
+
query GetFilteredSortedPaginated($after: Cursor) {
|
|
405
|
+
storeAccounts(
|
|
406
|
+
filter: {
|
|
407
|
+
balance: { gt: "1000" }
|
|
408
|
+
},
|
|
409
|
+
orderBy: [BALANCE_DESC, ASSET_ID_ASC],
|
|
410
|
+
first: 10,
|
|
411
|
+
after: $after
|
|
412
|
+
) {
|
|
413
|
+
edges {
|
|
414
|
+
node {
|
|
415
|
+
assetId
|
|
416
|
+
account
|
|
417
|
+
balance
|
|
418
|
+
}
|
|
419
|
+
cursor
|
|
420
|
+
}
|
|
421
|
+
pageInfo {
|
|
422
|
+
hasNextPage
|
|
423
|
+
hasPreviousPage
|
|
424
|
+
startCursor
|
|
425
|
+
endCursor
|
|
426
|
+
}
|
|
427
|
+
totalCount
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
```
|
|
431
|
+
|
|
432
|
+
> 📖 **详细过滤功能文档**: 查看 [高级过滤和查询功能使用指南](./ADVANCED_FILTERING_GUIDE.md) 了解所有支持的操作符、使用示例和最佳实践。
|
|
433
|
+
|
|
434
|
+
## 🏗️ 架构说明
|
|
435
|
+
|
|
436
|
+
### 工作原理
|
|
437
|
+
|
|
438
|
+
```
|
|
439
|
+
sui-rust-indexer 数据库
|
|
440
|
+
↓
|
|
441
|
+
[数据库内省器]
|
|
442
|
+
↓
|
|
443
|
+
[PostGraphile]
|
|
444
|
+
↓
|
|
445
|
+
[GraphQL API]
|
|
446
|
+
↓
|
|
447
|
+
[WebSocket]
|
|
448
|
+
```
|
|
449
|
+
|
|
450
|
+
1. **数据库扫描**: 启动时自动扫描数据库中的所有表
|
|
451
|
+
2. **结构解析**: 从 `table_fields` 元数据表读取动态表结构
|
|
452
|
+
3. **Schema 生成**: PostGraphile 基于表结构自动生成 GraphQL schema
|
|
453
|
+
4. **API 服务**: 提供完整的 GraphQL CRUD 操作和订阅功能
|
|
454
|
+
|
|
455
|
+
### 支持的表类型
|
|
456
|
+
|
|
457
|
+
1. **系统表**:
|
|
458
|
+
- `__dubheStoreTransactions` - 交易记录
|
|
459
|
+
- `__dubheStoreSchemas` - Schema 数据
|
|
460
|
+
- `__dubheStoreEvents` - 事件记录
|
|
461
|
+
- `table_fields` - 表结构元数据
|
|
462
|
+
|
|
463
|
+
2. **动态表**:
|
|
464
|
+
- `store_*` - 根据 `sui-rust-indexer` 配置动态创建的表
|
|
465
|
+
|
|
466
|
+
## 🚀 部署
|
|
467
|
+
|
|
468
|
+
### Docker 部署
|
|
469
|
+
|
|
470
|
+
```bash
|
|
471
|
+
# 使用提供的 docker-compose
|
|
472
|
+
docker-compose up -d
|
|
473
|
+
```
|
|
474
|
+
|
|
475
|
+
### 手动部署
|
|
476
|
+
|
|
477
|
+
```bash
|
|
478
|
+
# 构建项目
|
|
479
|
+
pnpm build
|
|
480
|
+
|
|
481
|
+
# 设置环境变量
|
|
482
|
+
export DATABASE_URL="postgres://..."
|
|
483
|
+
export PORT=4000
|
|
484
|
+
|
|
485
|
+
# 启动服务器
|
|
486
|
+
pnpm start
|
|
487
|
+
```
|
|
488
|
+
|
|
489
|
+
## 🔧 配置选项
|
|
490
|
+
|
|
491
|
+
### PostGraphile 特性
|
|
492
|
+
|
|
493
|
+
- ✅ **自动 CRUD**: 所有表自动支持增删改查
|
|
494
|
+
- ✅ **关系查询**: 自动处理表之间的关系
|
|
495
|
+
- ✅ **分页**: Relay 风格的连接分页
|
|
496
|
+
- ✅ **订阅**: GraphQL 订阅和 Live Queries
|
|
497
|
+
- ✅ **过滤排序**: 强大的查询条件和排序
|
|
498
|
+
- ✅ **权限控制**: 基于 PostgreSQL 的行级安全
|
|
499
|
+
|
|
500
|
+
### 自定义配置
|
|
501
|
+
|
|
502
|
+
在 `src/index.ts` 中可以修改 PostGraphile 配置:
|
|
503
|
+
|
|
504
|
+
```typescript
|
|
505
|
+
const createPostGraphileConfig = (availableTables: string[]) => {
|
|
506
|
+
return {
|
|
507
|
+
// 添加插件
|
|
508
|
+
appendPlugins: [
|
|
509
|
+
require('@graphile-contrib/pg-simplify-inflector'),
|
|
510
|
+
require('postgraphile-plugin-connection-filter')
|
|
511
|
+
],
|
|
512
|
+
|
|
513
|
+
// 自定义命名
|
|
514
|
+
inflection: {
|
|
515
|
+
// 自定义表名映射
|
|
516
|
+
},
|
|
517
|
+
|
|
518
|
+
// 添加自定义字段
|
|
519
|
+
makeAddInflectorsPlugin: (inflectors) => {
|
|
520
|
+
// 自定义逻辑
|
|
521
|
+
}
|
|
522
|
+
};
|
|
523
|
+
};
|
|
524
|
+
```
|
|
525
|
+
|
|
526
|
+
## 🛡️ 安全配置
|
|
527
|
+
|
|
528
|
+
### 数据库权限
|
|
529
|
+
|
|
530
|
+
```sql
|
|
531
|
+
-- 创建只读用户
|
|
532
|
+
CREATE USER graphql_readonly WITH PASSWORD 'secure_password';
|
|
533
|
+
|
|
534
|
+
-- 授予查询权限
|
|
535
|
+
GRANT CONNECT ON DATABASE sui_indexer TO graphql_readonly;
|
|
536
|
+
GRANT USAGE ON SCHEMA public TO graphql_readonly;
|
|
537
|
+
GRANT SELECT ON ALL TABLES IN SCHEMA public TO graphql_readonly;
|
|
538
|
+
|
|
539
|
+
-- 如需写入权限
|
|
540
|
+
GRANT INSERT, UPDATE, DELETE ON specific_tables TO graphql_readonly;
|
|
541
|
+
```
|
|
542
|
+
|
|
543
|
+
### 生产环境配置
|
|
544
|
+
|
|
545
|
+
```env
|
|
546
|
+
NODE_ENV=production
|
|
547
|
+
ENABLE_CORS=false
|
|
548
|
+
# 或设置特定来源
|
|
549
|
+
CORS_ORIGIN=https://yourdomain.com
|
|
550
|
+
```
|
|
551
|
+
|
|
552
|
+
## 📋 故障排除
|
|
553
|
+
|
|
554
|
+
### 常见问题
|
|
555
|
+
|
|
556
|
+
1. **数据库连接失败**
|
|
557
|
+
```
|
|
558
|
+
解决方案:检查 DATABASE_URL 和数据库服务状态
|
|
559
|
+
```
|
|
560
|
+
|
|
561
|
+
2. **表扫描为空**
|
|
562
|
+
```
|
|
563
|
+
解决方案:确保 sui-rust-indexer 已运行并创建了表
|
|
564
|
+
```
|
|
565
|
+
|
|
566
|
+
3. **schema 生成失败**
|
|
567
|
+
```
|
|
568
|
+
解决方案:检查 table_fields 表是否存在且有数据
|
|
569
|
+
```
|
|
570
|
+
|
|
571
|
+
4. **WebSocket 连接失败**
|
|
572
|
+
```
|
|
573
|
+
解决方案:检查防火墙设置和 ENABLE_SUBSCRIPTIONS 配置
|
|
574
|
+
```
|
|
575
|
+
|
|
576
|
+
### 调试模式
|
|
577
|
+
|
|
578
|
+
```bash
|
|
579
|
+
# 启用详细日志
|
|
580
|
+
DEBUG=postgraphile:* pnpm dev
|
|
581
|
+
|
|
582
|
+
# 查看生成的 schema
|
|
583
|
+
ls -la *.graphql
|
|
584
|
+
```
|
|
585
|
+
|
|
586
|
+
## 🤝 集成指南
|
|
587
|
+
|
|
588
|
+
### 与 sui-rust-indexer 集成
|
|
589
|
+
|
|
590
|
+
1. **启动顺序**: 先启动 `sui-rust-indexer`,再启动 GraphQL 服务器
|
|
591
|
+
2. **数据库共享**: 两个服务共享同一个 PostgreSQL 数据库
|
|
592
|
+
3. **配置同步**: 确保数据库连接配置一致
|
|
593
|
+
|
|
594
|
+
### 与前端集成
|
|
595
|
+
|
|
596
|
+
```typescript
|
|
597
|
+
// Apollo Client 配置
|
|
598
|
+
import { ApolloClient, InMemoryCache, split, HttpLink } from '@apollo/client';
|
|
599
|
+
import { GraphQLWsLink } from '@apollo/client/link/subscriptions';
|
|
600
|
+
import { createClient } from 'graphql-ws';
|
|
601
|
+
|
|
602
|
+
const httpLink = new HttpLink({
|
|
603
|
+
uri: 'http://localhost:4000/graphql',
|
|
604
|
+
});
|
|
605
|
+
|
|
606
|
+
const wsLink = new GraphQLWsLink(createClient({
|
|
607
|
+
url: 'ws://localhost:4000/graphql',
|
|
608
|
+
}));
|
|
609
|
+
|
|
610
|
+
const splitLink = split(
|
|
611
|
+
({ query }) => {
|
|
612
|
+
const definition = getMainDefinition(query);
|
|
613
|
+
return (
|
|
614
|
+
definition.kind === 'OperationDefinition' &&
|
|
615
|
+
definition.operation === 'subscription'
|
|
616
|
+
);
|
|
617
|
+
},
|
|
618
|
+
wsLink,
|
|
619
|
+
httpLink,
|
|
620
|
+
);
|
|
621
|
+
|
|
622
|
+
const client = new ApolloClient({
|
|
623
|
+
link: splitLink,
|
|
624
|
+
cache: new InMemoryCache(),
|
|
625
|
+
});
|
|
626
|
+
```
|
|
627
|
+
|
|
628
|
+
## 📄 许可证
|
|
629
|
+
|
|
630
|
+
MIT License
|
|
631
|
+
|
|
632
|
+
## WebSocket Subscription 支持
|
|
633
|
+
|
|
634
|
+
本服务器现已支持通过 WebSocket 进行实时数据订阅,使用 PostgreSQL 的 LISTEN/NOTIFY 机制。
|
|
635
|
+
|
|
636
|
+
### 环境变量配置
|
|
637
|
+
|
|
638
|
+
创建 `.env` 文件并配置以下变量:
|
|
639
|
+
|
|
640
|
+
```bash
|
|
641
|
+
# 数据库连接 URL
|
|
642
|
+
# 注意:对于 WebSocket 订阅,请使用直接连接而不是连接池
|
|
643
|
+
DATABASE_URL=postgres://postgres:postgres@127.0.0.1:5432/postgres
|
|
644
|
+
|
|
645
|
+
# 服务器端口
|
|
646
|
+
PORT=4000
|
|
647
|
+
|
|
648
|
+
# 环境模式
|
|
649
|
+
NODE_ENV=development
|
|
650
|
+
|
|
651
|
+
# GraphQL 端点路径
|
|
652
|
+
GRAPHQL_ENDPOINT=/graphql
|
|
653
|
+
|
|
654
|
+
# PostgreSQL Schema
|
|
655
|
+
PG_SCHEMA=public
|
|
656
|
+
|
|
657
|
+
# 启用 CORS
|
|
658
|
+
ENABLE_CORS=true
|
|
659
|
+
|
|
660
|
+
# 启用 WebSocket 订阅
|
|
661
|
+
# 设置为 true 以启用实时订阅功能
|
|
662
|
+
ENABLE_SUBSCRIPTIONS=true
|
|
663
|
+
```
|
|
664
|
+
|
|
665
|
+
### 订阅类型
|
|
666
|
+
|
|
667
|
+
1. **特定 Store 表订阅** - 自动为每个 `store_*` 表生成订阅
|
|
668
|
+
2. **所有 Store 表订阅** - 订阅所有 store 表的变更
|
|
669
|
+
3. **任意表订阅** - 订阅任意表的变更
|
|
670
|
+
4. **系统事件订阅** - 订阅系统级事件
|
|
671
|
+
|
|
672
|
+
### 测试订阅
|
|
673
|
+
|
|
674
|
+
```bash
|
|
675
|
+
# 安装依赖
|
|
676
|
+
npm install
|
|
677
|
+
|
|
678
|
+
# 启动服务器
|
|
679
|
+
npm run dev
|
|
680
|
+
|
|
681
|
+
# 在另一个终端测试订阅
|
|
682
|
+
npm run test:subscription
|
|
683
|
+
```
|
|
684
|
+
|
|
685
|
+
### 使用示例
|
|
686
|
+
|
|
687
|
+
在 GraphiQL 中运行:
|
|
688
|
+
|
|
689
|
+
```graphql
|
|
690
|
+
subscription {
|
|
691
|
+
allStoresChanged {
|
|
692
|
+
event
|
|
693
|
+
table
|
|
694
|
+
timestamp
|
|
695
|
+
data
|
|
696
|
+
id
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
```
|
|
700
|
+
|
|
701
|
+
详细使用指南请参考 [SUBSCRIPTION_USAGE.md](./SUBSCRIPTION_USAGE.md)。
|
|
702
|
+
|
|
703
|
+
### 注意事项
|
|
704
|
+
|
|
705
|
+
1. WebSocket 订阅不兼容 Neon 连接池,请使用直接数据库连接
|
|
706
|
+
2. 确保 PostgreSQL 支持 LISTEN/NOTIFY
|
|
707
|
+
3. sui-rust-indexer 会自动创建必要的触发器
|
|
708
|
+
4. 大量订阅可能影响性能,请合理使用
|
|
709
|
+
|
|
710
|
+
## 📄 许可证
|
|
711
|
+
|
|
712
|
+
MIT License
|
|
713
|
+
|
|
714
|
+
---
|
|
715
|
+
|
|
716
|
+
💡 **提示**: 这个服务器设计为 `sui-rust-indexer` 的完美伴侣,提供强大的 GraphQL 接口来访问索引的数据。无需手动配置 schema,一切都是自动的!
|
|
717
|
+
|
|
718
|
+
# 🔧 主要特性
|
|
719
|
+
|
|
720
|
+
- 🚀 **自动扫描数据库表结构**:无需手动配置,自动适配 sui-rust-indexer 的动态表
|
|
721
|
+
- 📊 **完整的 GraphQL API**:为所有表自动生成 CRUD 操作
|
|
722
|
+
- 📡 **实时订阅支持**:WebSocket 订阅数据变更
|
|
723
|
+
- 🎮 **增强版 GraphQL Playground**:现代化的查询界面,支持 Schema Explorer 和代码导出
|
|
724
|
+
- 🔍 **智能过滤和分页**:支持复杂查询条件
|
|
725
|
+
- 🎯 **开发友好**:提供详细的欢迎页面和使用指南
|
|
726
|
+
- 📝 **结构化日志系统**:使用 Winston 提供专业的日志记录和监控
|
|
727
|
+
|
|
728
|
+
## 📋 环境要求
|
|
729
|
+
|
|
730
|
+
- Node.js 18.0.0+
|
|
731
|
+
- PostgreSQL 数据库(由 sui-rust-indexer 管理)
|
|
732
|
+
- TypeScript 5.0+
|
|
733
|
+
|
|
734
|
+
## 🚀 快速开始
|
|
735
|
+
|
|
736
|
+
### 1. 安装依赖
|
|
737
|
+
|
|
738
|
+
```bash
|
|
739
|
+
npm install
|
|
740
|
+
```
|
|
741
|
+
|
|
742
|
+
### 2. 配置环境变量
|
|
743
|
+
|
|
744
|
+
复制并编辑环境变量文件:
|
|
745
|
+
|
|
746
|
+
```bash
|
|
747
|
+
cp .env.example .env
|
|
748
|
+
```
|
|
749
|
+
|
|
750
|
+
主要配置项:
|
|
751
|
+
|
|
752
|
+
```bash
|
|
753
|
+
# 数据库连接
|
|
754
|
+
DATABASE_URL=postgres://postgres:postgres@127.0.0.1:5432/postgres
|
|
755
|
+
|
|
756
|
+
# 服务器配置
|
|
757
|
+
PORT=4000
|
|
758
|
+
GRAPHQL_ENDPOINT=/graphql
|
|
759
|
+
PG_SCHEMA=public
|
|
760
|
+
|
|
761
|
+
# 功能开关
|
|
762
|
+
ENABLE_CORS=true
|
|
763
|
+
ENABLE_SUBSCRIPTIONS=true
|
|
764
|
+
REALTIME_PORT=4001
|
|
765
|
+
|
|
766
|
+
# 日志配置
|
|
767
|
+
LOG_LEVEL=info # error, warn, info, debug, verbose
|
|
768
|
+
```
|
|
769
|
+
|
|
770
|
+
### 3. 启动服务器
|
|
771
|
+
|
|
772
|
+
```bash
|
|
773
|
+
# 开发模式(支持热重载)
|
|
774
|
+
npm run dev
|
|
775
|
+
|
|
776
|
+
# 生产模式
|
|
777
|
+
npm run build
|
|
778
|
+
npm start
|
|
779
|
+
```
|
|
780
|
+
|
|
781
|
+
### 4. 访问服务
|
|
782
|
+
|
|
783
|
+
- 🏠 **主页**:http://localhost:4000 - 服务器信息和使用指南
|
|
784
|
+
- 🎮 **GraphQL Playground**:http://localhost:4000/playground - 现代化查询界面
|
|
785
|
+
- 📊 **GraphQL API**:http://localhost:4000/graphql - API 端点
|
|
786
|
+
- 📡 **WebSocket 订阅**:ws://localhost:4000/graphql - 实时订阅
|
|
787
|
+
|
|
788
|
+
## 📊 日志系统
|
|
789
|
+
|
|
790
|
+
本项目使用专业的 Winston 日志系统,提供结构化的日志记录:
|
|
791
|
+
|
|
792
|
+
### 主要特性
|
|
793
|
+
|
|
794
|
+
- 🎨 **彩色输出**:不同级别使用不同颜色
|
|
795
|
+
- 📁 **文件记录**:自动保存到 `logs/` 目录
|
|
796
|
+
- 🏷️ **组件标识**:明确标识日志来源
|
|
797
|
+
- 📊 **结构化数据**:支持附加元数据
|
|
798
|
+
- ⚡ **性能监控**:内置性能指标记录
|
|
799
|
+
- 🔒 **敏感信息保护**:自动隐藏密码等敏感信息
|
|
800
|
+
|
|
801
|
+
### 日志级别
|
|
802
|
+
|
|
803
|
+
```bash
|
|
804
|
+
export LOG_LEVEL=debug # 显示所有级别的日志
|
|
805
|
+
export LOG_LEVEL=info # 默认级别,生产环境推荐
|
|
806
|
+
export LOG_LEVEL=warn # 只显示警告和错误
|
|
807
|
+
```
|
|
808
|
+
|
|
809
|
+
### 日志文件
|
|
810
|
+
|
|
811
|
+
- `logs/combined.log`:所有日志(JSON格式)
|
|
812
|
+
- `logs/error.log`:错误日志
|
|
813
|
+
- `logs/exceptions.log`:未捕获异常
|
|
814
|
+
- `logs/rejections.log`:Promise拒绝
|
|
815
|
+
|
|
816
|
+
详细使用说明请参考:[LOGGING.md](./LOGGING.md)
|
|
817
|
+
|
|
818
|
+
## 🎮 使用 GraphQL Playground
|
|
819
|
+
|
|
820
|
+
访问 http://localhost:4000/playground 体验增强版 GraphQL Playground:
|
|
821
|
+
|
|
822
|
+
### 主要功能
|
|
823
|
+
|
|
824
|
+
- 📊 **Schema Explorer**:可视化浏览 GraphQL Schema
|
|
825
|
+
- 🔍 **智能补全**:自动补全查询语句
|
|
826
|
+
- 📝 **查询历史**:保存和管理查询历史
|
|
827
|
+
- 📋 **代码导出**:支持多种语言的代码生成
|
|
828
|
+
- 🎨 **现代化界面**:美观的用户界面
|
|
829
|
+
|
|
830
|
+
### 示例查询
|
|
831
|
+
|
|
832
|
+
```graphql
|
|
833
|
+
# 查询所有动态表
|
|
834
|
+
{
|
|
835
|
+
__schema {
|
|
836
|
+
queryType {
|
|
837
|
+
fields {
|
|
838
|
+
name
|
|
839
|
+
description
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
# 如果有 store_accounts 表
|
|
846
|
+
{
|
|
847
|
+
allStoreAccounts(first: 10) {
|
|
848
|
+
edges {
|
|
849
|
+
node {
|
|
850
|
+
id
|
|
851
|
+
# 其他字段根据表结构动态生成
|
|
852
|
+
}
|
|
853
|
+
}
|
|
854
|
+
}
|
|
855
|
+
}
|
|
856
|
+
```
|
|
857
|
+
|
|
858
|
+
### 实时订阅
|
|
859
|
+
|
|
860
|
+
如果启用了订阅功能,可以使用实时订阅:
|
|
861
|
+
|
|
862
|
+
```graphql
|
|
863
|
+
subscription {
|
|
864
|
+
allStoresChanged {
|
|
865
|
+
event
|
|
866
|
+
table
|
|
867
|
+
data
|
|
868
|
+
timestamp
|
|
869
|
+
}
|
|
870
|
+
}
|
|
871
|
+
```
|
|
872
|
+
|
|
873
|
+
## 🔧 配置选项
|
|
874
|
+
|
|
875
|
+
### 数据库配置
|
|
876
|
+
|
|
877
|
+
```bash
|
|
878
|
+
DATABASE_URL=postgres://username:password@host:port/database
|
|
879
|
+
PG_SCHEMA=public # 要扫描的数据库模式
|
|
880
|
+
```
|
|
881
|
+
|
|
882
|
+
### 服务器配置
|
|
883
|
+
|
|
884
|
+
```bash
|
|
885
|
+
PORT=4000 # HTTP 服务器端口
|
|
886
|
+
GRAPHQL_ENDPOINT=/graphql # GraphQL API 路径
|
|
887
|
+
ENABLE_CORS=true # 是否启用 CORS
|
|
888
|
+
```
|
|
889
|
+
|
|
890
|
+
### 订阅配置
|
|
891
|
+
|
|
892
|
+
```bash
|
|
893
|
+
ENABLE_SUBSCRIPTIONS=true # 是否启用订阅功能
|
|
894
|
+
REALTIME_PORT=4001 # WebSocket 服务器端口
|
|
895
|
+
```
|
|
896
|
+
|
|
897
|
+
### 日志配置
|
|
898
|
+
|
|
899
|
+
```bash
|
|
900
|
+
LOG_LEVEL=info # 日志级别
|
|
901
|
+
LOG_TO_FILE=true # 是否保存到文件
|
|
902
|
+
LOG_DIR=./logs # 日志文件目录
|
|
903
|
+
|
|
904
|
+
# PostGraphile SQL查询日志控制
|
|
905
|
+
DISABLE_QUERY_LOG=false # 设置为true禁用SQL查询日志
|
|
906
|
+
ENABLE_QUERY_LOG=false # 生产环境中设置为true启用查询日志
|
|
907
|
+
QUERY_TIMEOUT=30000 # GraphQL查询超时时间(毫秒)
|
|
908
|
+
```
|