@undefineds.co/xpod 0.3.31 → 0.3.32

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 (135) hide show
  1. package/dist/api/auth/AuthContext.d.ts +3 -2
  2. package/dist/api/auth/AuthContext.js +2 -1
  3. package/dist/api/auth/AuthContext.js.map +1 -1
  4. package/dist/api/auth/ClientCredentialsAuthenticator.d.ts +2 -12
  5. package/dist/api/auth/ClientCredentialsAuthenticator.js +4 -4
  6. package/dist/api/auth/ClientCredentialsAuthenticator.js.map +1 -1
  7. package/dist/api/auth/ServiceTokenAuthenticator.d.ts +2 -2
  8. package/dist/api/auth/ServiceTokenAuthenticator.js.map +1 -1
  9. package/dist/api/container/business-token.d.ts +1 -1
  10. package/dist/api/container/business-token.js +5 -1
  11. package/dist/api/container/business-token.js.map +1 -1
  12. package/dist/api/container/common.js +14 -10
  13. package/dist/api/container/common.js.map +1 -1
  14. package/dist/api/container/routes.js +16 -3
  15. package/dist/api/container/routes.js.map +1 -1
  16. package/dist/api/container/types.d.ts +2 -4
  17. package/dist/api/container/types.js.map +1 -1
  18. package/dist/api/handlers/ChatHandler.d.ts +1 -1
  19. package/dist/api/handlers/ChatHandler.js +1 -1
  20. package/dist/api/handlers/ChatHandler.js.map +1 -1
  21. package/dist/api/handlers/EdgeNodeSignalHandler.js +3 -1
  22. package/dist/api/handlers/EdgeNodeSignalHandler.js.map +1 -1
  23. package/dist/api/handlers/PodManagementHandler.d.ts +2 -0
  24. package/dist/api/handlers/PodManagementHandler.js +114 -12
  25. package/dist/api/handlers/PodManagementHandler.js.map +1 -1
  26. package/dist/api/handlers/ProvisionHandler.d.ts +27 -0
  27. package/dist/api/handlers/ProvisionHandler.js +339 -32
  28. package/dist/api/handlers/ProvisionHandler.js.map +1 -1
  29. package/dist/api/handlers/QuotaHandler.js +0 -12
  30. package/dist/api/handlers/QuotaHandler.js.map +1 -1
  31. package/dist/api/handlers/index.d.ts +0 -1
  32. package/dist/api/handlers/index.js +0 -1
  33. package/dist/api/handlers/index.js.map +1 -1
  34. package/dist/api/runtime.js +3 -3
  35. package/dist/api/runtime.js.map +1 -1
  36. package/dist/components/context.jsonld +12 -0
  37. package/dist/edge/EdgeNodeAgent.d.ts +1 -1
  38. package/dist/edge/EdgeNodeAgent.js +1 -1
  39. package/dist/edge/EdgeNodeAgent.js.map +1 -1
  40. package/dist/edge/EdgeNodeDnsCoordinator.d.ts +1 -0
  41. package/dist/edge/EdgeNodeDnsCoordinator.js +9 -3
  42. package/dist/edge/EdgeNodeDnsCoordinator.js.map +1 -1
  43. package/dist/edge/EdgeNodeDnsCoordinator.jsonld +4 -0
  44. package/dist/edge/EdgeNodeHealthProbeService.d.ts +3 -0
  45. package/dist/edge/EdgeNodeHealthProbeService.js +22 -2
  46. package/dist/edge/EdgeNodeHealthProbeService.js.map +1 -1
  47. package/dist/edge/EdgeNodeHealthProbeService.jsonld +12 -0
  48. package/dist/http/ClusterIngressRouter.js +6 -3
  49. package/dist/http/ClusterIngressRouter.js.map +1 -1
  50. package/dist/http/ClusterWebSocketConfigurator.js +6 -2
  51. package/dist/http/ClusterWebSocketConfigurator.js.map +1 -1
  52. package/dist/http/EdgeNodeDirectDebugHttpHandler.d.ts +2 -0
  53. package/dist/http/EdgeNodeDirectDebugHttpHandler.js +18 -3
  54. package/dist/http/EdgeNodeDirectDebugHttpHandler.js.map +1 -1
  55. package/dist/http/EdgeNodeDirectDebugHttpHandler.jsonld +8 -0
  56. package/dist/http/EdgeNodeProxyHttpHandler.js +6 -2
  57. package/dist/http/EdgeNodeProxyHttpHandler.js.map +1 -1
  58. package/dist/http/cluster/PodMigrationHttpHandler.d.ts +2 -2
  59. package/dist/http/cluster/PodMigrationHttpHandler.js +2 -2
  60. package/dist/http/cluster/PodMigrationHttpHandler.js.map +1 -1
  61. package/dist/http/quota/QuotaAdminHttpHandler.js +27 -21
  62. package/dist/http/quota/QuotaAdminHttpHandler.js.map +1 -1
  63. package/dist/identity/drizzle/AccountRepository.d.ts +4 -22
  64. package/dist/identity/drizzle/AccountRepository.js +9 -113
  65. package/dist/identity/drizzle/AccountRepository.js.map +1 -1
  66. package/dist/identity/drizzle/AccountRoleRepository.d.ts +5 -5
  67. package/dist/identity/drizzle/AccountRoleRepository.js +204 -97
  68. package/dist/identity/drizzle/AccountRoleRepository.js.map +1 -1
  69. package/dist/identity/drizzle/DdnsRepository.d.ts +5 -20
  70. package/dist/identity/drizzle/DdnsRepository.js +13 -49
  71. package/dist/identity/drizzle/DdnsRepository.js.map +1 -1
  72. package/dist/identity/drizzle/EdgeNodeRepository.d.ts +13 -6
  73. package/dist/identity/drizzle/EdgeNodeRepository.js +167 -66
  74. package/dist/identity/drizzle/EdgeNodeRepository.js.map +1 -1
  75. package/dist/identity/drizzle/PodLookupRepository.d.ts +7 -36
  76. package/dist/identity/drizzle/PodLookupRepository.js +103 -126
  77. package/dist/identity/drizzle/PodLookupRepository.js.map +1 -1
  78. package/dist/identity/drizzle/ServiceTokenRepository.d.ts +13 -1
  79. package/dist/identity/drizzle/ServiceTokenRepository.js +7 -0
  80. package/dist/identity/drizzle/ServiceTokenRepository.js.map +1 -1
  81. package/dist/identity/drizzle/db.d.ts +2 -1
  82. package/dist/identity/drizzle/db.js +173 -297
  83. package/dist/identity/drizzle/db.js.map +1 -1
  84. package/dist/identity/drizzle/schema.pg.d.ts +3 -11
  85. package/dist/identity/drizzle/schema.pg.js +10 -45
  86. package/dist/identity/drizzle/schema.pg.js.map +1 -1
  87. package/dist/identity/drizzle/schema.sqlite.d.ts +88 -531
  88. package/dist/identity/drizzle/schema.sqlite.js +13 -46
  89. package/dist/identity/drizzle/schema.sqlite.js.map +1 -1
  90. package/dist/identity/oidc/ScopedPickWebIdHandler.d.ts +3 -0
  91. package/dist/identity/oidc/ScopedPickWebIdHandler.js +18 -6
  92. package/dist/identity/oidc/ScopedPickWebIdHandler.js.map +1 -1
  93. package/dist/identity/oidc/ScopedPickWebIdHandler.jsonld +22 -0
  94. package/dist/provision/ProvisionCodeCodec.js +10 -1
  95. package/dist/provision/ProvisionCodeCodec.js.map +1 -1
  96. package/dist/provision/ProvisionPodCreator.d.ts +8 -2
  97. package/dist/provision/ProvisionPodCreator.js +134 -41
  98. package/dist/provision/ProvisionPodCreator.js.map +1 -1
  99. package/dist/provision/ProvisionPodCreator.jsonld +38 -3
  100. package/dist/quota/DrizzleQuotaService.d.ts +0 -4
  101. package/dist/quota/DrizzleQuotaService.js +1 -21
  102. package/dist/quota/DrizzleQuotaService.js.map +1 -1
  103. package/dist/quota/DrizzleQuotaService.jsonld +0 -16
  104. package/dist/quota/NoopQuotaService.d.ts +0 -4
  105. package/dist/quota/NoopQuotaService.js +0 -8
  106. package/dist/quota/NoopQuotaService.js.map +1 -1
  107. package/dist/quota/NoopQuotaService.jsonld +0 -16
  108. package/dist/quota/QuotaService.d.ts +0 -4
  109. package/dist/quota/QuotaService.js.map +1 -1
  110. package/dist/quota/QuotaService.jsonld +0 -16
  111. package/dist/service/EdgeNodeSignalClient.d.ts +0 -2
  112. package/dist/service/EdgeNodeSignalClient.js +0 -4
  113. package/dist/service/EdgeNodeSignalClient.js.map +1 -1
  114. package/dist/service/PodMigrationService.d.ts +2 -2
  115. package/dist/service/PodMigrationService.js +4 -4
  116. package/dist/service/PodMigrationService.js.map +1 -1
  117. package/dist/setup/LocalSetupServiceTokenRepository.d.ts +22 -0
  118. package/dist/setup/LocalSetupServiceTokenRepository.js +68 -0
  119. package/dist/setup/LocalSetupServiceTokenRepository.js.map +1 -0
  120. package/dist/storage/quota/PerAccountQuotaStrategy.js +2 -2
  121. package/dist/storage/quota/PerAccountQuotaStrategy.js.map +1 -1
  122. package/dist/storage/quota/UsageRepository.d.ts +10 -32
  123. package/dist/storage/quota/UsageRepository.js +84 -281
  124. package/dist/storage/quota/UsageRepository.js.map +1 -1
  125. package/dist/subdomain/SubdomainService.d.ts +1 -1
  126. package/dist/subdomain/SubdomainService.js +1 -1
  127. package/dist/subdomain/SubdomainService.js.map +1 -1
  128. package/dist/subdomain/SubdomainService.jsonld +1 -1
  129. package/package.json +1 -1
  130. package/dist/api/handlers/ApiKeyHandler.d.ts +0 -15
  131. package/dist/api/handlers/ApiKeyHandler.js +0 -153
  132. package/dist/api/handlers/ApiKeyHandler.js.map +0 -1
  133. package/dist/api/store/DrizzleClientCredentialsStore.d.ts +0 -51
  134. package/dist/api/store/DrizzleClientCredentialsStore.js +0 -115
  135. package/dist/api/store/DrizzleClientCredentialsStore.js.map +0 -1
@@ -6,10 +6,14 @@ const drizzle_orm_1 = require("drizzle-orm");
6
6
  const db_1 = require("./db");
7
7
  const schema_1 = require("./schema");
8
8
  class EdgeNodeRepository {
9
- constructor(db) {
9
+ constructor(db, options = {}) {
10
10
  this.db = db;
11
+ this.ready = options.ensureClusterTables === false
12
+ ? Promise.resolve()
13
+ : (0, db_1.ensureCloudClusterTables)(db);
11
14
  }
12
15
  async listNodes() {
16
+ await this.ready;
13
17
  const result = await (0, db_1.executeQuery)(this.db, (0, drizzle_orm_1.sql) `
14
18
  SELECT en.id,
15
19
  en.display_name,
@@ -18,24 +22,20 @@ class EdgeNodeRepository {
18
22
  en.updated_at,
19
23
  en.last_seen,
20
24
  en.metadata,
21
- COALESCE(pods.count, 0) AS pod_count
22
- FROM identity_edge_node en
23
- LEFT JOIN (
24
- SELECT node_id, COUNT(*) AS count
25
- FROM identity_edge_node_pod
26
- GROUP BY node_id
27
- ) pods ON pods.node_id = en.id
25
+ en.pod_base_urls
26
+ FROM cluster_node en
28
27
  ORDER BY en.created_at ASC
29
28
  `);
30
29
  return result.rows.map((row) => {
31
30
  const createdAt = (0, db_1.fromDbTimestamp)(row.created_at);
32
31
  const updatedAt = (0, db_1.fromDbTimestamp)(row.updated_at);
33
32
  const lastSeen = (0, db_1.fromDbTimestamp)(row.last_seen);
33
+ const podBaseUrls = parsePodBaseUrls(row.pod_base_urls);
34
34
  return {
35
35
  nodeId: String(row.id),
36
36
  displayName: row.display_name == null ? undefined : String(row.display_name),
37
37
  nodeType: (['center', 'edge', 'sp'].includes(row.node_type) ? row.node_type : 'edge'),
38
- podCount: Number(row.pod_count ?? 0),
38
+ podCount: podBaseUrls.length,
39
39
  createdAt: createdAt?.toISOString(),
40
40
  updatedAt: updatedAt?.toISOString(),
41
41
  lastSeen: lastSeen?.toISOString(),
@@ -43,14 +43,15 @@ class EdgeNodeRepository {
43
43
  };
44
44
  });
45
45
  }
46
- async createNode(displayName, _accountId) {
46
+ async createNode(displayName) {
47
+ await this.ready;
47
48
  const nodeId = (0, node_crypto_1.randomUUID)();
48
49
  const token = (0, node_crypto_1.randomBytes)(32).toString('base64url');
49
50
  const tokenHash = (0, node_crypto_1.createHash)('sha256').update(token).digest('hex');
50
51
  const now = new Date();
51
52
  const ts = (0, db_1.toDbTimestamp)(this.db, now);
52
53
  await (0, db_1.executeStatement)(this.db, (0, drizzle_orm_1.sql) `
53
- INSERT INTO identity_edge_node (id, display_name, token_hash, created_at, updated_at)
54
+ INSERT INTO cluster_node (id, display_name, token_hash, created_at, updated_at)
54
55
  VALUES (${nodeId}, ${displayName ?? null}, ${tokenHash}, ${ts}, ${ts})
55
56
  `);
56
57
  return {
@@ -59,16 +60,11 @@ class EdgeNodeRepository {
59
60
  createdAt: now.toISOString(),
60
61
  };
61
62
  }
62
- /**
63
- * Node/account 关系待产品化后单独建模;当前阶段不再在节点表上持久化账号归属。
64
- */
65
- async getNodeOwner(_nodeId) {
66
- return undefined;
67
- }
68
63
  async getNodeSecret(nodeId) {
64
+ await this.ready;
69
65
  const result = await (0, db_1.executeQuery)(this.db, (0, drizzle_orm_1.sql) `
70
66
  SELECT id, display_name, token_hash, node_type, metadata
71
- FROM identity_edge_node
67
+ FROM cluster_node
72
68
  WHERE id = ${nodeId}
73
69
  LIMIT 1
74
70
  `);
@@ -85,10 +81,11 @@ class EdgeNodeRepository {
85
81
  };
86
82
  }
87
83
  async updateNodeHeartbeat(nodeId, metadata, timestamp) {
84
+ await this.ready;
88
85
  const payload = metadata == null ? null : JSON.stringify(metadata);
89
86
  const ts = (0, db_1.toDbTimestamp)(this.db, timestamp);
90
87
  await (0, db_1.executeStatement)(this.db, (0, drizzle_orm_1.sql) `
91
- UPDATE identity_edge_node
88
+ UPDATE cluster_node
92
89
  SET metadata = ${payload},
93
90
  last_seen = ${ts},
94
91
  updated_at = ${ts}
@@ -96,11 +93,12 @@ class EdgeNodeRepository {
96
93
  `);
97
94
  }
98
95
  async updateNodeMode(nodeId, options) {
96
+ await this.ready;
99
97
  const capabilitiesPayload = options.capabilities ? JSON.stringify(options.capabilities) : null;
100
98
  const now = new Date();
101
99
  const ts = (0, db_1.toDbTimestamp)(this.db, now);
102
100
  await (0, db_1.executeStatement)(this.db, (0, drizzle_orm_1.sql) `
103
- UPDATE identity_edge_node
101
+ UPDATE cluster_node
104
102
  SET access_mode = ${options.accessMode},
105
103
  ipv4 = ${options.ipv4 ?? null},
106
104
  public_port = ${options.publicPort ?? null},
@@ -113,10 +111,11 @@ class EdgeNodeRepository {
113
111
  `);
114
112
  }
115
113
  async getNodeConnectivityInfo(nodeId) {
114
+ await this.ready;
116
115
  const result = await (0, db_1.executeQuery)(this.db, (0, drizzle_orm_1.sql) `
117
- SELECT id, access_mode, ipv4, public_port, subdomain,
116
+ SELECT id, access_mode, ipv4, public_port, public_url, subdomain,
118
117
  connectivity_status, last_connectivity_check
119
- FROM identity_edge_node
118
+ FROM cluster_node
120
119
  WHERE id = ${nodeId}
121
120
  LIMIT 1
122
121
  `);
@@ -129,12 +128,14 @@ class EdgeNodeRepository {
129
128
  accessMode: row.access_mode ? String(row.access_mode) : undefined,
130
129
  ipv4: row.ipv4 ? String(row.ipv4) : undefined,
131
130
  publicPort: row.public_port ? Number(row.public_port) : undefined,
131
+ publicUrl: row.public_url ? String(row.public_url) : undefined,
132
132
  subdomain: row.subdomain ? String(row.subdomain) : undefined,
133
133
  connectivityStatus: row.connectivity_status ? String(row.connectivity_status) : undefined,
134
134
  lastConnectivityCheck: (0, db_1.fromDbTimestamp)(row.last_connectivity_check),
135
135
  };
136
136
  }
137
137
  async mergeNodeMetadata(nodeId, patch) {
138
+ await this.ready;
138
139
  // Read current metadata
139
140
  const current = await this.getNodeMetadata(nodeId);
140
141
  if (!current) {
@@ -145,16 +146,17 @@ class EdgeNodeRepository {
145
146
  const payload = JSON.stringify(merged);
146
147
  const ts = (0, db_1.toDbTimestamp)(this.db, new Date());
147
148
  await (0, db_1.executeStatement)(this.db, (0, drizzle_orm_1.sql) `
148
- UPDATE identity_edge_node
149
+ UPDATE cluster_node
149
150
  SET metadata = ${payload},
150
151
  updated_at = ${ts}
151
152
  WHERE id = ${nodeId}
152
153
  `);
153
154
  }
154
155
  async getNodeMetadata(nodeId) {
156
+ await this.ready;
155
157
  const result = await (0, db_1.executeQuery)(this.db, (0, drizzle_orm_1.sql) `
156
158
  SELECT id, metadata, last_seen
157
- FROM identity_edge_node
159
+ FROM cluster_node
158
160
  WHERE id = ${nodeId}
159
161
  LIMIT 1
160
162
  `);
@@ -169,49 +171,63 @@ class EdgeNodeRepository {
169
171
  };
170
172
  }
171
173
  async replaceNodePods(nodeId, pods) {
172
- await this.db.transaction(async (tx) => {
173
- await tx.execute((0, drizzle_orm_1.sql) `DELETE FROM identity_edge_node_pod WHERE node_id = ${nodeId}`);
174
- if (pods.length > 0) {
175
- const values = pods.map((baseUrl) => (0, drizzle_orm_1.sql) `(${nodeId}, ${baseUrl})`);
176
- await tx.execute((0, drizzle_orm_1.sql) `
177
- INSERT INTO identity_edge_node_pod (node_id, base_url)
178
- VALUES ${drizzle_orm_1.sql.join(values, (0, drizzle_orm_1.sql) `, `)}
179
- ON CONFLICT DO NOTHING
180
- `);
174
+ await this.ready;
175
+ const targetPods = uniquePodBaseUrls(pods);
176
+ await this.updateNodePodBaseUrls(nodeId, targetPods);
177
+ await this.removePodBaseUrlsFromOtherNodes(nodeId, targetPods);
178
+ }
179
+ async assignPodToNode(nodeId, baseUrl) {
180
+ await this.ready;
181
+ const normalizedBaseUrl = normalizePodBaseUrl(baseUrl);
182
+ if (!normalizedBaseUrl) {
183
+ return;
184
+ }
185
+ const rows = await this.getNodePodRows();
186
+ for (const row of rows) {
187
+ const current = parsePodBaseUrls(row.pod_base_urls);
188
+ const next = row.id === nodeId
189
+ ? uniquePodBaseUrls([...current, normalizedBaseUrl])
190
+ : current.filter((value) => value !== normalizedBaseUrl);
191
+ if (!arraysEqual(current, next)) {
192
+ await this.updateNodePodBaseUrls(row.id, next);
181
193
  }
182
- });
194
+ }
183
195
  }
184
196
  async findNodeByResourcePath(path) {
197
+ await this.ready;
185
198
  const result = await (0, db_1.executeQuery)(this.db, (0, drizzle_orm_1.sql) `
186
- SELECT en.id,
187
- en.access_mode,
188
- en.metadata,
189
- pods.base_url
190
- FROM identity_edge_node_pod pods
191
- JOIN identity_edge_node en ON en.id = pods.node_id
192
- WHERE ${path} LIKE pods.base_url || '%'
193
- ORDER BY length(pods.base_url) DESC
194
- LIMIT 1
199
+ SELECT id, access_mode, public_url, metadata, pod_base_urls
200
+ FROM cluster_node
201
+ WHERE pod_base_urls IS NOT NULL AND pod_base_urls <> ''
195
202
  `);
196
- if (result.rows.length === 0) {
203
+ let bestMatch;
204
+ for (const row of result.rows) {
205
+ for (const baseUrl of parsePodBaseUrls(row.pod_base_urls)) {
206
+ if (path.startsWith(baseUrl) && baseUrl.length > (bestMatch?.baseUrl.length ?? 0)) {
207
+ bestMatch = { row, baseUrl };
208
+ }
209
+ }
210
+ }
211
+ if (!bestMatch) {
197
212
  return undefined;
198
213
  }
199
- const row = result.rows[0];
200
214
  return {
201
- nodeId: String(row.id),
202
- baseUrl: String(row.base_url),
203
- accessMode: row.access_mode ? String(row.access_mode) : undefined,
204
- metadata: row.metadata ?? null,
215
+ nodeId: String(bestMatch.row.id),
216
+ baseUrl: bestMatch.baseUrl,
217
+ accessMode: bestMatch.row.access_mode ? String(bestMatch.row.access_mode) : undefined,
218
+ publicUrl: bestMatch.row.public_url ? String(bestMatch.row.public_url) : undefined,
219
+ metadata: parseJsonRecord(bestMatch.row.metadata),
205
220
  };
206
221
  }
207
222
  async findNodeBySubdomain(hostname) {
223
+ await this.ready;
208
224
  const normalized = hostname.trim().toLowerCase();
209
225
  if (normalized.length === 0) {
210
226
  return undefined;
211
227
  }
212
228
  const result = await (0, db_1.executeQuery)(this.db, (0, drizzle_orm_1.sql) `
213
- SELECT id, access_mode, metadata, subdomain
214
- FROM identity_edge_node
229
+ SELECT id, access_mode, public_url, metadata, subdomain
230
+ FROM cluster_node
215
231
  WHERE subdomain = ${normalized}
216
232
  LIMIT 1
217
233
  `);
@@ -222,6 +238,7 @@ class EdgeNodeRepository {
222
238
  return {
223
239
  nodeId: String(row.id),
224
240
  accessMode: row.access_mode ? String(row.access_mode) : undefined,
241
+ publicUrl: row.public_url ? String(row.public_url) : undefined,
225
242
  metadata: row.metadata ?? null,
226
243
  subdomain: row.subdomain ? String(row.subdomain) : undefined,
227
244
  };
@@ -246,6 +263,7 @@ class EdgeNodeRepository {
246
263
  * Get node capabilities and related information for admin queries
247
264
  */
248
265
  async getNodeCapabilities(nodeId) {
266
+ await this.ready;
249
267
  const row = await this.db
250
268
  .select({
251
269
  id: schema_1.edgeNodes.id,
@@ -276,6 +294,7 @@ class EdgeNodeRepository {
276
294
  * List all nodes with their capability information
277
295
  */
278
296
  async listNodeCapabilities() {
297
+ await this.ready;
279
298
  const rows = await this.db
280
299
  .select({
281
300
  id: schema_1.edgeNodes.id,
@@ -305,12 +324,13 @@ class EdgeNodeRepository {
305
324
  * Center nodes use the same table as edge nodes but with nodeType='center'.
306
325
  */
307
326
  async registerCenterNode(options) {
327
+ await this.ready;
308
328
  const token = (0, node_crypto_1.randomBytes)(32).toString('base64url');
309
329
  const tokenHash = (0, node_crypto_1.createHash)('sha256').update(token).digest('hex');
310
330
  const now = Math.floor(Date.now() / 1000); // Unix timestamp for SQLite compatibility
311
331
  // Use upsert pattern: INSERT ... ON CONFLICT UPDATE
312
332
  await (0, db_1.executeStatement)(this.db, (0, drizzle_orm_1.sql) `
313
- INSERT INTO identity_edge_node (
333
+ INSERT INTO cluster_node (
314
334
  id, display_name, token_hash, node_type, internal_ip, internal_port,
315
335
  connectivity_status, created_at, updated_at, last_seen
316
336
  )
@@ -331,9 +351,10 @@ class EdgeNodeRepository {
331
351
  * Update center node heartbeat with internal endpoint info.
332
352
  */
333
353
  async updateCenterNodeHeartbeat(nodeId, internalIp, internalPort, timestamp) {
354
+ await this.ready;
334
355
  const ts = Math.floor(timestamp.getTime() / 1000); // Unix timestamp for SQLite compatibility
335
356
  await (0, db_1.executeStatement)(this.db, (0, drizzle_orm_1.sql) `
336
- UPDATE identity_edge_node
357
+ UPDATE cluster_node
337
358
  SET internal_ip = ${internalIp},
338
359
  internal_port = ${internalPort},
339
360
  last_seen = ${ts},
@@ -346,9 +367,10 @@ class EdgeNodeRepository {
346
367
  * List all center nodes in the cluster.
347
368
  */
348
369
  async listCenterNodes() {
370
+ await this.ready;
349
371
  const result = await (0, db_1.executeQuery)(this.db, (0, drizzle_orm_1.sql) `
350
372
  SELECT id, display_name, internal_ip, internal_port, connectivity_status, last_seen
351
- FROM identity_edge_node
373
+ FROM cluster_node
352
374
  WHERE node_type = 'center'
353
375
  ORDER BY created_at ASC
354
376
  `);
@@ -365,9 +387,10 @@ class EdgeNodeRepository {
365
387
  * Get a specific center node by ID.
366
388
  */
367
389
  async getCenterNode(nodeId) {
390
+ await this.ready;
368
391
  const result = await (0, db_1.executeQuery)(this.db, (0, drizzle_orm_1.sql) `
369
392
  SELECT id, display_name, internal_ip, internal_port, connectivity_status, last_seen
370
- FROM identity_edge_node
393
+ FROM cluster_node
371
394
  WHERE id = ${nodeId} AND node_type = 'center'
372
395
  LIMIT 1
373
396
  `);
@@ -388,9 +411,10 @@ class EdgeNodeRepository {
388
411
  * Find a center node by its internal endpoint (for routing).
389
412
  */
390
413
  async findCenterNodeByEndpoint(internalIp, internalPort) {
414
+ await this.ready;
391
415
  const result = await (0, db_1.executeQuery)(this.db, (0, drizzle_orm_1.sql) `
392
416
  SELECT id, display_name, internal_ip, internal_port, connectivity_status, last_seen
393
- FROM identity_edge_node
417
+ FROM cluster_node
394
418
  WHERE node_type = 'center' AND internal_ip = ${internalIp} AND internal_port = ${internalPort}
395
419
  LIMIT 1
396
420
  `);
@@ -411,9 +435,10 @@ class EdgeNodeRepository {
411
435
  * Mark a center node as unreachable (for health checks).
412
436
  */
413
437
  async markCenterNodeUnreachable(nodeId) {
438
+ await this.ready;
414
439
  const ts = (0, db_1.toDbTimestamp)(this.db, new Date());
415
440
  await (0, db_1.executeStatement)(this.db, (0, drizzle_orm_1.sql) `
416
- UPDATE identity_edge_node
441
+ UPDATE cluster_node
417
442
  SET connectivity_status = 'unreachable',
418
443
  updated_at = ${ts}
419
444
  WHERE id = ${nodeId} AND node_type = 'center'
@@ -423,9 +448,10 @@ class EdgeNodeRepository {
423
448
  * Remove a center node from the cluster.
424
449
  */
425
450
  async removeCenterNode(nodeId) {
451
+ await this.ready;
426
452
  // Note: For SQLite, we can't easily get affected row count, so just execute and return true
427
453
  await (0, db_1.executeStatement)(this.db, (0, drizzle_orm_1.sql) `
428
- DELETE FROM identity_edge_node
454
+ DELETE FROM cluster_node
429
455
  WHERE id = ${nodeId} AND node_type = 'center'
430
456
  `);
431
457
  return true;
@@ -435,6 +461,7 @@ class EdgeNodeRepository {
435
461
  * List nodes owned by a specific account
436
462
  */
437
463
  async listNodesByAccount(accountId) {
464
+ await this.ready;
438
465
  void accountId;
439
466
  return [];
440
467
  }
@@ -442,18 +469,51 @@ class EdgeNodeRepository {
442
469
  * Delete a node
443
470
  */
444
471
  async deleteNode(nodeId) {
445
- // First delete associated pods
446
- await (0, db_1.executeStatement)(this.db, (0, drizzle_orm_1.sql) `
447
- DELETE FROM identity_edge_node_pod WHERE node_id = ${nodeId}
448
- `);
449
- // Then delete the node
472
+ await this.ready;
450
473
  const result = await (0, db_1.executeQuery)(this.db, (0, drizzle_orm_1.sql) `
451
- DELETE FROM identity_edge_node
474
+ DELETE FROM cluster_node
452
475
  WHERE id = ${nodeId}
453
476
  RETURNING id
454
477
  `);
455
478
  return result.rows.length > 0;
456
479
  }
480
+ async getNodePodRows() {
481
+ const result = await (0, db_1.executeQuery)(this.db, (0, drizzle_orm_1.sql) `
482
+ SELECT id, pod_base_urls
483
+ FROM cluster_node
484
+ `);
485
+ return result.rows.map((row) => ({
486
+ id: String(row.id),
487
+ pod_base_urls: row.pod_base_urls,
488
+ }));
489
+ }
490
+ async updateNodePodBaseUrls(nodeId, pods) {
491
+ const payload = pods.length > 0 ? JSON.stringify(pods) : null;
492
+ const ts = (0, db_1.toDbTimestamp)(this.db, new Date());
493
+ await (0, db_1.executeStatement)(this.db, (0, drizzle_orm_1.sql) `
494
+ UPDATE cluster_node
495
+ SET pod_base_urls = ${payload},
496
+ updated_at = ${ts}
497
+ WHERE id = ${nodeId}
498
+ `);
499
+ }
500
+ async removePodBaseUrlsFromOtherNodes(nodeId, pods) {
501
+ if (pods.length === 0) {
502
+ return;
503
+ }
504
+ const podSet = new Set(pods);
505
+ const rows = await this.getNodePodRows();
506
+ for (const row of rows) {
507
+ if (row.id === nodeId) {
508
+ continue;
509
+ }
510
+ const current = parsePodBaseUrls(row.pod_base_urls);
511
+ const next = current.filter((value) => !podSet.has(value));
512
+ if (!arraysEqual(current, next)) {
513
+ await this.updateNodePodBaseUrls(row.id, next);
514
+ }
515
+ }
516
+ }
457
517
  // ============ SP (Storage Provider) Node Methods ============
458
518
  /**
459
519
  * Register or update an SP node (UPSERT by nodeId).
@@ -463,6 +523,7 @@ class EdgeNodeRepository {
463
523
  * 不传 nodeId 则 Cloud 随机分配。
464
524
  */
465
525
  async registerSpNode(options) {
526
+ await this.ready;
466
527
  const nodeId = options.nodeId || (0, node_crypto_1.randomUUID)();
467
528
  const nodeToken = options.nodeToken || (0, node_crypto_1.randomBytes)(32).toString('base64url');
468
529
  const nodeTokenHash = (0, node_crypto_1.createHash)('sha256').update(nodeToken).digest('hex');
@@ -470,7 +531,7 @@ class EdgeNodeRepository {
470
531
  const now = new Date();
471
532
  const ts = (0, db_1.toDbTimestamp)(this.db, now);
472
533
  await (0, db_1.executeStatement)(this.db, (0, drizzle_orm_1.sql) `
473
- INSERT INTO identity_edge_node (
534
+ INSERT INTO cluster_node (
474
535
  id, display_name, token_hash, service_token_hash,
475
536
  node_type, public_url,
476
537
  connectivity_status, created_at, updated_at
@@ -497,9 +558,10 @@ class EdgeNodeRepository {
497
558
  * Get SP node info by nodeId.
498
559
  */
499
560
  async getSpNode(nodeId) {
561
+ await this.ready;
500
562
  const result = await (0, db_1.executeQuery)(this.db, (0, drizzle_orm_1.sql) `
501
563
  SELECT id, display_name, public_url, service_token_hash, last_seen
502
- FROM identity_edge_node
564
+ FROM cluster_node
503
565
  WHERE id = ${nodeId} AND node_type = 'sp'
504
566
  LIMIT 1
505
567
  `);
@@ -517,4 +579,43 @@ class EdgeNodeRepository {
517
579
  }
518
580
  }
519
581
  exports.EdgeNodeRepository = EdgeNodeRepository;
582
+ function parsePodBaseUrls(value) {
583
+ if (Array.isArray(value)) {
584
+ return uniquePodBaseUrls(value);
585
+ }
586
+ if (typeof value !== 'string' || value.length === 0) {
587
+ return [];
588
+ }
589
+ try {
590
+ const parsed = JSON.parse(value);
591
+ return Array.isArray(parsed) ? uniquePodBaseUrls(parsed) : [];
592
+ }
593
+ catch {
594
+ return [];
595
+ }
596
+ }
597
+ function normalizePodBaseUrl(value) {
598
+ return typeof value === 'string' && value.length > 0 ? value : undefined;
599
+ }
600
+ function uniquePodBaseUrls(values) {
601
+ return [...new Set(values.map(normalizePodBaseUrl).filter((value) => Boolean(value)))];
602
+ }
603
+ function arraysEqual(left, right) {
604
+ return left.length === right.length && left.every((value, index) => value === right[index]);
605
+ }
606
+ function parseJsonRecord(value) {
607
+ if (!value) {
608
+ return null;
609
+ }
610
+ if (typeof value === 'string') {
611
+ try {
612
+ const parsed = JSON.parse(value);
613
+ return parsed && typeof parsed === 'object' && !Array.isArray(parsed) ? parsed : null;
614
+ }
615
+ catch {
616
+ return null;
617
+ }
618
+ }
619
+ return typeof value === 'object' && !Array.isArray(value) ? value : null;
620
+ }
520
621
  //# sourceMappingURL=EdgeNodeRepository.js.map