@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.
- package/dist/api/auth/AuthContext.d.ts +3 -2
- package/dist/api/auth/AuthContext.js +2 -1
- package/dist/api/auth/AuthContext.js.map +1 -1
- package/dist/api/auth/ClientCredentialsAuthenticator.d.ts +2 -12
- package/dist/api/auth/ClientCredentialsAuthenticator.js +4 -4
- package/dist/api/auth/ClientCredentialsAuthenticator.js.map +1 -1
- package/dist/api/auth/ServiceTokenAuthenticator.d.ts +2 -2
- package/dist/api/auth/ServiceTokenAuthenticator.js.map +1 -1
- package/dist/api/container/business-token.d.ts +1 -1
- package/dist/api/container/business-token.js +5 -1
- package/dist/api/container/business-token.js.map +1 -1
- package/dist/api/container/common.js +14 -10
- package/dist/api/container/common.js.map +1 -1
- package/dist/api/container/routes.js +16 -3
- package/dist/api/container/routes.js.map +1 -1
- package/dist/api/container/types.d.ts +2 -4
- package/dist/api/container/types.js.map +1 -1
- package/dist/api/handlers/ChatHandler.d.ts +1 -1
- package/dist/api/handlers/ChatHandler.js +1 -1
- package/dist/api/handlers/ChatHandler.js.map +1 -1
- package/dist/api/handlers/EdgeNodeSignalHandler.js +3 -1
- package/dist/api/handlers/EdgeNodeSignalHandler.js.map +1 -1
- package/dist/api/handlers/PodManagementHandler.d.ts +2 -0
- package/dist/api/handlers/PodManagementHandler.js +114 -12
- package/dist/api/handlers/PodManagementHandler.js.map +1 -1
- package/dist/api/handlers/ProvisionHandler.d.ts +27 -0
- package/dist/api/handlers/ProvisionHandler.js +339 -32
- package/dist/api/handlers/ProvisionHandler.js.map +1 -1
- package/dist/api/handlers/QuotaHandler.js +0 -12
- package/dist/api/handlers/QuotaHandler.js.map +1 -1
- package/dist/api/handlers/index.d.ts +0 -1
- package/dist/api/handlers/index.js +0 -1
- package/dist/api/handlers/index.js.map +1 -1
- package/dist/api/runtime.js +3 -3
- package/dist/api/runtime.js.map +1 -1
- package/dist/components/context.jsonld +12 -0
- package/dist/edge/EdgeNodeAgent.d.ts +1 -1
- package/dist/edge/EdgeNodeAgent.js +1 -1
- package/dist/edge/EdgeNodeAgent.js.map +1 -1
- package/dist/edge/EdgeNodeDnsCoordinator.d.ts +1 -0
- package/dist/edge/EdgeNodeDnsCoordinator.js +9 -3
- package/dist/edge/EdgeNodeDnsCoordinator.js.map +1 -1
- package/dist/edge/EdgeNodeDnsCoordinator.jsonld +4 -0
- package/dist/edge/EdgeNodeHealthProbeService.d.ts +3 -0
- package/dist/edge/EdgeNodeHealthProbeService.js +22 -2
- package/dist/edge/EdgeNodeHealthProbeService.js.map +1 -1
- package/dist/edge/EdgeNodeHealthProbeService.jsonld +12 -0
- package/dist/http/ClusterIngressRouter.js +6 -3
- package/dist/http/ClusterIngressRouter.js.map +1 -1
- package/dist/http/ClusterWebSocketConfigurator.js +6 -2
- package/dist/http/ClusterWebSocketConfigurator.js.map +1 -1
- package/dist/http/EdgeNodeDirectDebugHttpHandler.d.ts +2 -0
- package/dist/http/EdgeNodeDirectDebugHttpHandler.js +18 -3
- package/dist/http/EdgeNodeDirectDebugHttpHandler.js.map +1 -1
- package/dist/http/EdgeNodeDirectDebugHttpHandler.jsonld +8 -0
- package/dist/http/EdgeNodeProxyHttpHandler.js +6 -2
- package/dist/http/EdgeNodeProxyHttpHandler.js.map +1 -1
- package/dist/http/cluster/PodMigrationHttpHandler.d.ts +2 -2
- package/dist/http/cluster/PodMigrationHttpHandler.js +2 -2
- package/dist/http/cluster/PodMigrationHttpHandler.js.map +1 -1
- package/dist/http/quota/QuotaAdminHttpHandler.js +27 -21
- package/dist/http/quota/QuotaAdminHttpHandler.js.map +1 -1
- package/dist/identity/drizzle/AccountRepository.d.ts +4 -22
- package/dist/identity/drizzle/AccountRepository.js +9 -113
- package/dist/identity/drizzle/AccountRepository.js.map +1 -1
- package/dist/identity/drizzle/AccountRoleRepository.d.ts +5 -5
- package/dist/identity/drizzle/AccountRoleRepository.js +204 -97
- package/dist/identity/drizzle/AccountRoleRepository.js.map +1 -1
- package/dist/identity/drizzle/DdnsRepository.d.ts +5 -20
- package/dist/identity/drizzle/DdnsRepository.js +13 -49
- package/dist/identity/drizzle/DdnsRepository.js.map +1 -1
- package/dist/identity/drizzle/EdgeNodeRepository.d.ts +13 -6
- package/dist/identity/drizzle/EdgeNodeRepository.js +167 -66
- package/dist/identity/drizzle/EdgeNodeRepository.js.map +1 -1
- package/dist/identity/drizzle/PodLookupRepository.d.ts +7 -36
- package/dist/identity/drizzle/PodLookupRepository.js +103 -126
- package/dist/identity/drizzle/PodLookupRepository.js.map +1 -1
- package/dist/identity/drizzle/ServiceTokenRepository.d.ts +13 -1
- package/dist/identity/drizzle/ServiceTokenRepository.js +7 -0
- package/dist/identity/drizzle/ServiceTokenRepository.js.map +1 -1
- package/dist/identity/drizzle/db.d.ts +2 -1
- package/dist/identity/drizzle/db.js +173 -297
- package/dist/identity/drizzle/db.js.map +1 -1
- package/dist/identity/drizzle/schema.pg.d.ts +3 -11
- package/dist/identity/drizzle/schema.pg.js +10 -45
- package/dist/identity/drizzle/schema.pg.js.map +1 -1
- package/dist/identity/drizzle/schema.sqlite.d.ts +88 -531
- package/dist/identity/drizzle/schema.sqlite.js +13 -46
- package/dist/identity/drizzle/schema.sqlite.js.map +1 -1
- package/dist/identity/oidc/ScopedPickWebIdHandler.d.ts +3 -0
- package/dist/identity/oidc/ScopedPickWebIdHandler.js +18 -6
- package/dist/identity/oidc/ScopedPickWebIdHandler.js.map +1 -1
- package/dist/identity/oidc/ScopedPickWebIdHandler.jsonld +22 -0
- package/dist/provision/ProvisionCodeCodec.js +10 -1
- package/dist/provision/ProvisionCodeCodec.js.map +1 -1
- package/dist/provision/ProvisionPodCreator.d.ts +8 -2
- package/dist/provision/ProvisionPodCreator.js +134 -41
- package/dist/provision/ProvisionPodCreator.js.map +1 -1
- package/dist/provision/ProvisionPodCreator.jsonld +38 -3
- package/dist/quota/DrizzleQuotaService.d.ts +0 -4
- package/dist/quota/DrizzleQuotaService.js +1 -21
- package/dist/quota/DrizzleQuotaService.js.map +1 -1
- package/dist/quota/DrizzleQuotaService.jsonld +0 -16
- package/dist/quota/NoopQuotaService.d.ts +0 -4
- package/dist/quota/NoopQuotaService.js +0 -8
- package/dist/quota/NoopQuotaService.js.map +1 -1
- package/dist/quota/NoopQuotaService.jsonld +0 -16
- package/dist/quota/QuotaService.d.ts +0 -4
- package/dist/quota/QuotaService.js.map +1 -1
- package/dist/quota/QuotaService.jsonld +0 -16
- package/dist/service/EdgeNodeSignalClient.d.ts +0 -2
- package/dist/service/EdgeNodeSignalClient.js +0 -4
- package/dist/service/EdgeNodeSignalClient.js.map +1 -1
- package/dist/service/PodMigrationService.d.ts +2 -2
- package/dist/service/PodMigrationService.js +4 -4
- package/dist/service/PodMigrationService.js.map +1 -1
- package/dist/setup/LocalSetupServiceTokenRepository.d.ts +22 -0
- package/dist/setup/LocalSetupServiceTokenRepository.js +68 -0
- package/dist/setup/LocalSetupServiceTokenRepository.js.map +1 -0
- package/dist/storage/quota/PerAccountQuotaStrategy.js +2 -2
- package/dist/storage/quota/PerAccountQuotaStrategy.js.map +1 -1
- package/dist/storage/quota/UsageRepository.d.ts +10 -32
- package/dist/storage/quota/UsageRepository.js +84 -281
- package/dist/storage/quota/UsageRepository.js.map +1 -1
- package/dist/subdomain/SubdomainService.d.ts +1 -1
- package/dist/subdomain/SubdomainService.js +1 -1
- package/dist/subdomain/SubdomainService.js.map +1 -1
- package/dist/subdomain/SubdomainService.jsonld +1 -1
- package/package.json +1 -1
- package/dist/api/handlers/ApiKeyHandler.d.ts +0 -15
- package/dist/api/handlers/ApiKeyHandler.js +0 -153
- package/dist/api/handlers/ApiKeyHandler.js.map +0 -1
- package/dist/api/store/DrizzleClientCredentialsStore.d.ts +0 -51
- package/dist/api/store/DrizzleClientCredentialsStore.js +0 -115
- 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
|
-
|
|
22
|
-
FROM
|
|
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:
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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.
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
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
|
|
187
|
-
|
|
188
|
-
|
|
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
|
-
|
|
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:
|
|
203
|
-
accessMode: row.access_mode ? String(row.access_mode) : undefined,
|
|
204
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|