@undefineds.co/xpod 0.2.5 → 0.2.6
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/handlers/EdgeNodeSignalHandler.js +2 -27
- package/dist/api/handlers/EdgeNodeSignalHandler.js.map +1 -1
- package/dist/api/handlers/NodeHandler.js +17 -158
- package/dist/api/handlers/NodeHandler.js.map +1 -1
- package/dist/identity/drizzle/EdgeNodeRepository.d.ts +3 -4
- package/dist/identity/drizzle/EdgeNodeRepository.js +10 -37
- package/dist/identity/drizzle/EdgeNodeRepository.js.map +1 -1
- package/dist/identity/drizzle/db.js +13 -2
- package/dist/identity/drizzle/db.js.map +1 -1
- package/dist/identity/drizzle/schema.pg.js +0 -1
- package/dist/identity/drizzle/schema.pg.js.map +1 -1
- package/dist/identity/drizzle/schema.sqlite.d.ts +0 -19
- package/dist/identity/drizzle/schema.sqlite.js +0 -1
- package/dist/identity/drizzle/schema.sqlite.js.map +1 -1
- package/package.json +1 -1
|
@@ -34,33 +34,8 @@ function registerEdgeNodeSignalRoutes(server, options) {
|
|
|
34
34
|
nodeId = (0, AuthContext_1.getNodeId)(auth);
|
|
35
35
|
}
|
|
36
36
|
else {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
if (!webId) {
|
|
40
|
-
sendJson(response, 400, { error: 'Cannot determine user' });
|
|
41
|
-
return;
|
|
42
|
-
}
|
|
43
|
-
nodeId = typeof payload.nodeId === 'string' ? payload.nodeId.trim() : '';
|
|
44
|
-
if (!nodeId) {
|
|
45
|
-
sendJson(response, 400, { error: 'nodeId is required' });
|
|
46
|
-
return;
|
|
47
|
-
}
|
|
48
|
-
try {
|
|
49
|
-
const owner = await repo.getNodeOwner(nodeId);
|
|
50
|
-
if (!owner) {
|
|
51
|
-
sendJson(response, 404, { error: 'Node not found' });
|
|
52
|
-
return;
|
|
53
|
-
}
|
|
54
|
-
if (owner !== webId) {
|
|
55
|
-
sendJson(response, 403, { error: 'Access denied' });
|
|
56
|
-
return;
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
catch (error) {
|
|
60
|
-
logger.error(`Failed to validate node access: ${error}`);
|
|
61
|
-
sendJson(response, 500, { error: 'Failed to validate node access' });
|
|
62
|
-
return;
|
|
63
|
-
}
|
|
37
|
+
sendJson(response, 501, { error: 'WebID/API-key based node signaling is temporarily unavailable' });
|
|
38
|
+
return;
|
|
64
39
|
}
|
|
65
40
|
const now = new Date();
|
|
66
41
|
try {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EdgeNodeSignalHandler.js","sourceRoot":"","sources":["../../../src/api/handlers/EdgeNodeSignalHandler.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ServerResponse } from 'node:http';\nimport { getLoggerFor } from 'global-logger-factory';\nimport type { AuthenticatedRequest } from '../middleware/AuthMiddleware';\nimport type { ApiServer } from '../ApiServer';\nimport type { EdgeNodeRepository } from '../../identity/drizzle/EdgeNodeRepository';\nimport type { EdgeNodeDnsCoordinator } from '../../edge/EdgeNodeDnsCoordinator';\nimport type { EdgeNodeHealthProbeService } from '../../edge/EdgeNodeHealthProbeService';\nimport { isNodeAuth, getNodeId, getWebId } from '../auth/AuthContext';\n\nexport interface EdgeNodeSignalHandlerOptions {\n repository: EdgeNodeRepository;\n dnsCoordinator?: EdgeNodeDnsCoordinator;\n healthProbeService?: EdgeNodeHealthProbeService;\n}\n\n/**\n * Handler for edge node signaling API\n *\n * POST /v1/signal - Edge node heartbeat/signal\n *\n * Requires API authentication and a nodeId in the request body.\n */\nexport function registerEdgeNodeSignalRoutes(server: ApiServer, options: EdgeNodeSignalHandlerOptions): void {\n const logger = getLoggerFor('EdgeNodeSignalHandler');\n const repo = options.repository;\n const { dnsCoordinator, healthProbeService } = options;\n\n // POST /v1/signal - authenticated via nodeToken, API key, or Solid token\n server.post('/v1/signal', async (request, response, _params) => {\n const auth = request.auth;\n if (!auth) {\n sendJson(response, 401, { error: 'Authentication required' });\n return;\n }\n\n const body = await readJsonBody(request);\n if (!body || typeof body !== 'object') {\n sendJson(response, 400, { error: 'Request body must be a JSON object' });\n return;\n }\n const payload = body as Record<string, unknown>;\n\n // Resolve nodeId based on auth type\n let nodeId: string;\n\n if (isNodeAuth(auth)) {\n // nodeToken 认证:nodeId 来自认证结果,无需 owner 检查\n nodeId = getNodeId(auth)!;\n } else {\n // WebID 认证:从 body 读 nodeId,检查 owner\n const webId = getWebId(auth);\n if (!webId) {\n sendJson(response, 400, { error: 'Cannot determine user' });\n return;\n }\n nodeId = typeof payload.nodeId === 'string' ? payload.nodeId.trim() : '';\n if (!nodeId) {\n sendJson(response, 400, { error: 'nodeId is required' });\n return;\n }\n try {\n const owner = await repo.getNodeOwner(nodeId);\n if (!owner) {\n sendJson(response, 404, { error: 'Node not found' });\n return;\n }\n if (owner !== webId) {\n sendJson(response, 403, { error: 'Access denied' });\n return;\n }\n } catch (error) {\n logger.error(`Failed to validate node access: ${error}`);\n sendJson(response, 500, { error: 'Failed to validate node access' });\n return;\n }\n }\n\n const now = new Date();\n\n try {\n // Get current metadata to merge\n const existing = await repo.getNodeMetadata(nodeId);\n let metadata = mergeMetadata(existing?.metadata ?? {}, payload, now);\n\n // 从 DB connectivity 列注入 subdomain/ipv4,供 dnsCoordinator 使用\n const connectivityInfo = await repo.getNodeConnectivityInfo(nodeId);\n if (connectivityInfo) {\n if (connectivityInfo.subdomain && !metadata.subdomain) {\n metadata.subdomain = connectivityInfo.subdomain;\n }\n if (connectivityInfo.ipv4 && !metadata.ipv4) {\n metadata.ipv4 = connectivityInfo.ipv4;\n }\n if (connectivityInfo.connectivityStatus && !metadata.connectivityStatus) {\n metadata.connectivityStatus = connectivityInfo.connectivityStatus;\n }\n }\n\n // Update heartbeat\n await repo.updateNodeHeartbeat(nodeId, metadata, now);\n\n // Update pods if provided\n if (Array.isArray(payload.pods)) {\n await repo.replaceNodePods(nodeId, payload.pods as string[]);\n }\n\n // 健康检查 → DNS 同步\n if (healthProbeService) {\n await healthProbeService.probeNode(nodeId);\n // 健康检查结果写入了 DB metadata,重新读取\n const freshMeta = await repo.getNodeMetadata(nodeId);\n if (freshMeta?.metadata) {\n const reachability = (freshMeta.metadata as Record<string, unknown>).reachability;\n if (reachability) {\n metadata.reachability = reachability;\n const status = (reachability as Record<string, unknown>).status;\n if (typeof status === 'string') {\n metadata.connectivityStatus = status === 'unreachable' ? 'unreachable' : 'reachable';\n }\n }\n }\n }\n\n if (dnsCoordinator) {\n await dnsCoordinator.synchronize(nodeId, metadata);\n }\n\n logger.debug(`Signal received from node ${nodeId}`);\n\n sendJson(response, 200, {\n status: 'ok',\n nodeId,\n lastSeen: now.toISOString(),\n metadata,\n });\n } catch (error) {\n logger.error(`Signal handling error for node ${nodeId}:`, error);\n if (error instanceof Error) {\n logger.error(`Stack trace: ${error.stack}`);\n }\n sendJson(response, 500, { error: 'Failed to process signal' });\n }\n });\n}\n\nfunction mergeMetadata(\n previous: Record<string, unknown>,\n payload: Record<string, unknown>,\n now: Date,\n): Record<string, unknown> {\n const next: Record<string, unknown> = { ...previous };\n next.lastHeartbeatAt = now.toISOString();\n\n const copyIfPresent = (key: string) => {\n if (payload[key] !== undefined) {\n next[key] = payload[key];\n }\n };\n\n copyIfPresent('baseUrl');\n copyIfPresent('publicAddress');\n copyIfPresent('hostname');\n copyIfPresent('ipv4');\n copyIfPresent('ipv6');\n copyIfPresent('version');\n copyIfPresent('status');\n copyIfPresent('capabilities');\n copyIfPresent('metrics');\n\n return next;\n}\n\nasync function readJsonBody(request: AuthenticatedRequest): Promise<unknown> {\n return new Promise((resolve, reject) => {\n let data = '';\n request.setEncoding('utf8');\n request.on('data', (chunk: string) => {\n data += chunk;\n });\n request.on('end', () => {\n if (!data) {\n resolve(undefined);\n return;\n }\n try {\n resolve(JSON.parse(data));\n } catch {\n resolve(undefined);\n }\n });\n request.on('error', reject);\n });\n}\n\nfunction sendJson(response: ServerResponse, status: number, data: unknown): void {\n response.statusCode = status;\n response.setHeader('Content-Type', 'application/json');\n response.end(JSON.stringify(data));\n}\n"]}
|
|
1
|
+
{"version":3,"file":"EdgeNodeSignalHandler.js","sourceRoot":"","sources":["../../../src/api/handlers/EdgeNodeSignalHandler.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ServerResponse } from 'node:http';\nimport { getLoggerFor } from 'global-logger-factory';\nimport type { AuthenticatedRequest } from '../middleware/AuthMiddleware';\nimport type { ApiServer } from '../ApiServer';\nimport type { EdgeNodeRepository } from '../../identity/drizzle/EdgeNodeRepository';\nimport type { EdgeNodeDnsCoordinator } from '../../edge/EdgeNodeDnsCoordinator';\nimport type { EdgeNodeHealthProbeService } from '../../edge/EdgeNodeHealthProbeService';\nimport { isNodeAuth, getNodeId } from '../auth/AuthContext';\n\nexport interface EdgeNodeSignalHandlerOptions {\n repository: EdgeNodeRepository;\n dnsCoordinator?: EdgeNodeDnsCoordinator;\n healthProbeService?: EdgeNodeHealthProbeService;\n}\n\n/**\n * Handler for edge node signaling API\n *\n * POST /v1/signal - Edge node heartbeat/signal\n *\n * Requires API authentication and a nodeId in the request body.\n */\nexport function registerEdgeNodeSignalRoutes(server: ApiServer, options: EdgeNodeSignalHandlerOptions): void {\n const logger = getLoggerFor('EdgeNodeSignalHandler');\n const repo = options.repository;\n const { dnsCoordinator, healthProbeService } = options;\n\n // POST /v1/signal - authenticated via nodeToken, API key, or Solid token\n server.post('/v1/signal', async (request, response, _params) => {\n const auth = request.auth;\n if (!auth) {\n sendJson(response, 401, { error: 'Authentication required' });\n return;\n }\n\n const body = await readJsonBody(request);\n if (!body || typeof body !== 'object') {\n sendJson(response, 400, { error: 'Request body must be a JSON object' });\n return;\n }\n const payload = body as Record<string, unknown>;\n\n // Resolve nodeId based on auth type\n let nodeId: string;\n\n if (isNodeAuth(auth)) {\n // nodeToken 认证:nodeId 来自认证结果,无需 owner 检查\n nodeId = getNodeId(auth)!;\n } else {\n sendJson(response, 501, { error: 'WebID/API-key based node signaling is temporarily unavailable' });\n return;\n }\n\n const now = new Date();\n\n try {\n // Get current metadata to merge\n const existing = await repo.getNodeMetadata(nodeId);\n let metadata = mergeMetadata(existing?.metadata ?? {}, payload, now);\n\n // 从 DB connectivity 列注入 subdomain/ipv4,供 dnsCoordinator 使用\n const connectivityInfo = await repo.getNodeConnectivityInfo(nodeId);\n if (connectivityInfo) {\n if (connectivityInfo.subdomain && !metadata.subdomain) {\n metadata.subdomain = connectivityInfo.subdomain;\n }\n if (connectivityInfo.ipv4 && !metadata.ipv4) {\n metadata.ipv4 = connectivityInfo.ipv4;\n }\n if (connectivityInfo.connectivityStatus && !metadata.connectivityStatus) {\n metadata.connectivityStatus = connectivityInfo.connectivityStatus;\n }\n }\n\n // Update heartbeat\n await repo.updateNodeHeartbeat(nodeId, metadata, now);\n\n // Update pods if provided\n if (Array.isArray(payload.pods)) {\n await repo.replaceNodePods(nodeId, payload.pods as string[]);\n }\n\n // 健康检查 → DNS 同步\n if (healthProbeService) {\n await healthProbeService.probeNode(nodeId);\n // 健康检查结果写入了 DB metadata,重新读取\n const freshMeta = await repo.getNodeMetadata(nodeId);\n if (freshMeta?.metadata) {\n const reachability = (freshMeta.metadata as Record<string, unknown>).reachability;\n if (reachability) {\n metadata.reachability = reachability;\n const status = (reachability as Record<string, unknown>).status;\n if (typeof status === 'string') {\n metadata.connectivityStatus = status === 'unreachable' ? 'unreachable' : 'reachable';\n }\n }\n }\n }\n\n if (dnsCoordinator) {\n await dnsCoordinator.synchronize(nodeId, metadata);\n }\n\n logger.debug(`Signal received from node ${nodeId}`);\n\n sendJson(response, 200, {\n status: 'ok',\n nodeId,\n lastSeen: now.toISOString(),\n metadata,\n });\n } catch (error) {\n logger.error(`Signal handling error for node ${nodeId}:`, error);\n if (error instanceof Error) {\n logger.error(`Stack trace: ${error.stack}`);\n }\n sendJson(response, 500, { error: 'Failed to process signal' });\n }\n });\n}\n\nfunction mergeMetadata(\n previous: Record<string, unknown>,\n payload: Record<string, unknown>,\n now: Date,\n): Record<string, unknown> {\n const next: Record<string, unknown> = { ...previous };\n next.lastHeartbeatAt = now.toISOString();\n\n const copyIfPresent = (key: string) => {\n if (payload[key] !== undefined) {\n next[key] = payload[key];\n }\n };\n\n copyIfPresent('baseUrl');\n copyIfPresent('publicAddress');\n copyIfPresent('hostname');\n copyIfPresent('ipv4');\n copyIfPresent('ipv6');\n copyIfPresent('version');\n copyIfPresent('status');\n copyIfPresent('capabilities');\n copyIfPresent('metrics');\n\n return next;\n}\n\nasync function readJsonBody(request: AuthenticatedRequest): Promise<unknown> {\n return new Promise((resolve, reject) => {\n let data = '';\n request.setEncoding('utf8');\n request.on('data', (chunk: string) => {\n data += chunk;\n });\n request.on('end', () => {\n if (!data) {\n resolve(undefined);\n return;\n }\n try {\n resolve(JSON.parse(data));\n } catch {\n resolve(undefined);\n }\n });\n request.on('error', reject);\n });\n}\n\nfunction sendJson(response: ServerResponse, status: number, data: unknown): void {\n response.statusCode = status;\n response.setHeader('Content-Type', 'application/json');\n response.end(JSON.stringify(data));\n}\n"]}
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.registerNodeRoutes = registerNodeRoutes;
|
|
4
4
|
const global_logger_factory_1 = require("global-logger-factory");
|
|
5
|
-
const AuthContext_1 = require("../auth/AuthContext");
|
|
6
5
|
/**
|
|
7
6
|
* Handler for edge node management API
|
|
8
7
|
*
|
|
@@ -15,171 +14,31 @@ const AuthContext_1 = require("../auth/AuthContext");
|
|
|
15
14
|
*/
|
|
16
15
|
function registerNodeRoutes(server, options) {
|
|
17
16
|
const logger = (0, global_logger_factory_1.getLoggerFor)('NodeHandler');
|
|
18
|
-
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
return true;
|
|
24
|
-
}
|
|
25
|
-
return false;
|
|
17
|
+
void options;
|
|
18
|
+
const sendUnavailable = (response) => {
|
|
19
|
+
sendJson(response, 501, {
|
|
20
|
+
error: 'Node management API is temporarily unavailable',
|
|
21
|
+
});
|
|
26
22
|
};
|
|
27
23
|
// GET /v1/nodes - List user's nodes
|
|
28
|
-
server.get('/v1/nodes', async (
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
}
|
|
32
|
-
const auth = request.auth;
|
|
33
|
-
const webId = (0, AuthContext_1.getWebId)(auth);
|
|
34
|
-
if (!webId) {
|
|
35
|
-
sendJson(response, 400, { error: 'Cannot determine user' });
|
|
36
|
-
return;
|
|
37
|
-
}
|
|
38
|
-
try {
|
|
39
|
-
// Use webId as owner identifier
|
|
40
|
-
const nodes = await repo.listNodesByAccount(webId);
|
|
41
|
-
sendJson(response, 200, {
|
|
42
|
-
nodes: nodes.map(formatNodeInfo),
|
|
43
|
-
total: nodes.length,
|
|
44
|
-
timestamp: new Date().toISOString(),
|
|
45
|
-
});
|
|
46
|
-
}
|
|
47
|
-
catch (error) {
|
|
48
|
-
logger.error(`Failed to list nodes: ${error}`);
|
|
49
|
-
sendJson(response, 500, { error: 'Failed to list nodes' });
|
|
50
|
-
}
|
|
24
|
+
server.get('/v1/nodes', async (_request, response, _params) => {
|
|
25
|
+
logger.info('Node management API is temporarily unavailable: GET /v1/nodes');
|
|
26
|
+
sendUnavailable(response);
|
|
51
27
|
});
|
|
52
28
|
// POST /api/nodes - Create a new node
|
|
53
|
-
server.post('/v1/nodes', async (
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
}
|
|
57
|
-
const auth = request.auth;
|
|
58
|
-
const webId = (0, AuthContext_1.getWebId)(auth);
|
|
59
|
-
console.log(`[NodeHandler] POST /v1/nodes - auth: ${JSON.stringify(auth)}, webId: ${webId}`);
|
|
60
|
-
if (!webId) {
|
|
61
|
-
sendJson(response, 400, { error: 'Cannot determine user' });
|
|
62
|
-
return;
|
|
63
|
-
}
|
|
64
|
-
const body = await readJsonBody(request);
|
|
65
|
-
const displayName = body && typeof body === 'object'
|
|
66
|
-
? body.displayName
|
|
67
|
-
: undefined;
|
|
68
|
-
try {
|
|
69
|
-
// Use webId as owner identifier
|
|
70
|
-
const result = await repo.createNode(displayName, webId);
|
|
71
|
-
logger.info(`Created node ${result.nodeId} for user ${webId}`);
|
|
72
|
-
sendJson(response, 201, {
|
|
73
|
-
success: true,
|
|
74
|
-
nodeId: result.nodeId,
|
|
75
|
-
token: result.token,
|
|
76
|
-
displayName,
|
|
77
|
-
createdAt: result.createdAt,
|
|
78
|
-
message: 'Edge node created. Save the token - it will not be shown again.',
|
|
79
|
-
});
|
|
80
|
-
}
|
|
81
|
-
catch (error) {
|
|
82
|
-
logger.error(`Failed to create node: ${error}`);
|
|
83
|
-
sendJson(response, 500, { error: 'Failed to create node' });
|
|
84
|
-
}
|
|
29
|
+
server.post('/v1/nodes', async (_request, response, _params) => {
|
|
30
|
+
logger.info('Node management API is temporarily unavailable: POST /v1/nodes');
|
|
31
|
+
sendUnavailable(response);
|
|
85
32
|
});
|
|
86
33
|
// GET /api/nodes/:nodeId - Get node info
|
|
87
|
-
server.get('/v1/nodes/:nodeId', async (
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
}
|
|
91
|
-
const auth = request.auth;
|
|
92
|
-
const webId = (0, AuthContext_1.getWebId)(auth);
|
|
93
|
-
const nodeId = decodeURIComponent(params.nodeId);
|
|
94
|
-
if (!webId) {
|
|
95
|
-
sendJson(response, 400, { error: 'Cannot determine user' });
|
|
96
|
-
return;
|
|
97
|
-
}
|
|
98
|
-
try {
|
|
99
|
-
// Check ownership
|
|
100
|
-
const nodeOwner = await repo.getNodeOwner(nodeId);
|
|
101
|
-
if (nodeOwner !== webId) {
|
|
102
|
-
sendJson(response, 403, { error: 'Access denied' });
|
|
103
|
-
return;
|
|
104
|
-
}
|
|
105
|
-
const nodeInfo = await repo.getNodeCapabilities(nodeId);
|
|
106
|
-
if (!nodeInfo) {
|
|
107
|
-
sendJson(response, 404, { error: 'Node not found' });
|
|
108
|
-
return;
|
|
109
|
-
}
|
|
110
|
-
sendJson(response, 200, formatNodeInfo(nodeInfo));
|
|
111
|
-
}
|
|
112
|
-
catch (error) {
|
|
113
|
-
logger.error(`Failed to get node: ${error}`);
|
|
114
|
-
sendJson(response, 500, { error: 'Failed to get node info' });
|
|
115
|
-
}
|
|
34
|
+
server.get('/v1/nodes/:nodeId', async (_request, response, _params) => {
|
|
35
|
+
logger.info('Node management API is temporarily unavailable: GET /v1/nodes/:nodeId');
|
|
36
|
+
sendUnavailable(response);
|
|
116
37
|
});
|
|
117
38
|
// DELETE /api/nodes/:nodeId - Delete a node
|
|
118
|
-
server.delete('/v1/nodes/:nodeId', async (
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
}
|
|
122
|
-
const auth = request.auth;
|
|
123
|
-
const webId = (0, AuthContext_1.getWebId)(auth);
|
|
124
|
-
const nodeId = decodeURIComponent(params.nodeId);
|
|
125
|
-
if (!webId) {
|
|
126
|
-
sendJson(response, 400, { error: 'Cannot determine user' });
|
|
127
|
-
return;
|
|
128
|
-
}
|
|
129
|
-
try {
|
|
130
|
-
// Check ownership
|
|
131
|
-
const nodeOwner = await repo.getNodeOwner(nodeId);
|
|
132
|
-
if (nodeOwner !== webId) {
|
|
133
|
-
sendJson(response, 403, { error: 'Access denied' });
|
|
134
|
-
return;
|
|
135
|
-
}
|
|
136
|
-
const deleted = await repo.deleteNode(nodeId);
|
|
137
|
-
if (!deleted) {
|
|
138
|
-
sendJson(response, 404, { error: 'Node not found' });
|
|
139
|
-
return;
|
|
140
|
-
}
|
|
141
|
-
logger.info(`Deleted node ${nodeId}`);
|
|
142
|
-
sendJson(response, 200, { status: 'deleted', nodeId });
|
|
143
|
-
}
|
|
144
|
-
catch (error) {
|
|
145
|
-
logger.error(`Failed to delete node: ${error}`);
|
|
146
|
-
sendJson(response, 500, { error: 'Failed to delete node' });
|
|
147
|
-
}
|
|
148
|
-
});
|
|
149
|
-
}
|
|
150
|
-
function formatNodeInfo(node) {
|
|
151
|
-
return {
|
|
152
|
-
nodeId: node.nodeId,
|
|
153
|
-
displayName: node.displayName,
|
|
154
|
-
accessMode: node.accessMode,
|
|
155
|
-
connectivityStatus: node.connectivityStatus,
|
|
156
|
-
lastSeen: node.lastSeen?.toISOString?.() ?? node.lastSeen,
|
|
157
|
-
capabilities: {
|
|
158
|
-
structured: node.capabilities,
|
|
159
|
-
strings: node.stringCapabilities,
|
|
160
|
-
},
|
|
161
|
-
};
|
|
162
|
-
}
|
|
163
|
-
async function readJsonBody(request) {
|
|
164
|
-
return new Promise((resolve, reject) => {
|
|
165
|
-
let data = '';
|
|
166
|
-
request.setEncoding('utf8');
|
|
167
|
-
request.on('data', (chunk) => {
|
|
168
|
-
data += chunk;
|
|
169
|
-
});
|
|
170
|
-
request.on('end', () => {
|
|
171
|
-
if (!data) {
|
|
172
|
-
resolve(undefined);
|
|
173
|
-
return;
|
|
174
|
-
}
|
|
175
|
-
try {
|
|
176
|
-
resolve(JSON.parse(data));
|
|
177
|
-
}
|
|
178
|
-
catch {
|
|
179
|
-
resolve(undefined);
|
|
180
|
-
}
|
|
181
|
-
});
|
|
182
|
-
request.on('error', reject);
|
|
39
|
+
server.delete('/v1/nodes/:nodeId', async (_request, response, _params) => {
|
|
40
|
+
logger.info('Node management API is temporarily unavailable: DELETE /v1/nodes/:nodeId');
|
|
41
|
+
sendUnavailable(response);
|
|
183
42
|
});
|
|
184
43
|
}
|
|
185
44
|
function sendJson(response, status, data) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NodeHandler.js","sourceRoot":"","sources":["../../../src/api/handlers/NodeHandler.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ServerResponse } from 'node:http';\nimport { getLoggerFor } from 'global-logger-factory';\nimport type { AuthenticatedRequest } from '../middleware/AuthMiddleware';\nimport type { ApiServer } from '../ApiServer';\nimport type { EdgeNodeRepository } from '../../identity/drizzle/EdgeNodeRepository';\nimport { getWebId, isSolidAuth } from '../auth/AuthContext';\n\nexport interface NodeHandlerOptions {\n repository: EdgeNodeRepository;\n}\n\n/**\n * Handler for edge node management API\n *\n * GET /v1/nodes - List user's nodes\n * POST /v1/nodes - Create a new node\n * GET /v1/nodes/:nodeId - Get node info\n * DELETE /v1/nodes/:nodeId - Delete a node\n *\n * All endpoints require Solid Token (user must have a Pod)\n */\nexport function registerNodeRoutes(server: ApiServer, options: NodeHandlerOptions): void {\n const logger = getLoggerFor('NodeHandler');\n const repo = options.repository;\n\n const rejectApiKey = (request: AuthenticatedRequest, response: ServerResponse): boolean => {\n const auth = request.auth;\n if (auth && isSolidAuth(auth) && auth.viaApiKey) {\n sendJson(response, 403, { error: 'API key is not allowed for this endpoint' });\n return true;\n }\n return false;\n };\n\n // GET /v1/nodes - List user's nodes\n server.get('/v1/nodes', async (request, response, _params) => {\n if (rejectApiKey(request, response)) {\n return;\n }\n const auth = request.auth!;\n const webId = getWebId(auth);\n\n if (!webId) {\n sendJson(response, 400, { error: 'Cannot determine user' });\n return;\n }\n\n try {\n // Use webId as owner identifier\n const nodes = await repo.listNodesByAccount(webId);\n\n sendJson(response, 200, {\n nodes: nodes.map(formatNodeInfo),\n total: nodes.length,\n timestamp: new Date().toISOString(),\n });\n } catch (error) {\n logger.error(`Failed to list nodes: ${error}`);\n sendJson(response, 500, { error: 'Failed to list nodes' });\n }\n });\n\n // POST /api/nodes - Create a new node\n server.post('/v1/nodes', async (request, response, _params) => {\n if (rejectApiKey(request, response)) {\n return;\n }\n const auth = request.auth!;\n const webId = getWebId(auth);\n\n console.log(`[NodeHandler] POST /v1/nodes - auth: ${JSON.stringify(auth)}, webId: ${webId}`);\n\n if (!webId) {\n sendJson(response, 400, { error: 'Cannot determine user' });\n return;\n }\n\n const body = await readJsonBody(request);\n const displayName = body && typeof body === 'object'\n ? (body as Record<string, unknown>).displayName as string | undefined\n : undefined;\n\n try {\n // Use webId as owner identifier\n const result = await repo.createNode(displayName, webId);\n\n logger.info(`Created node ${result.nodeId} for user ${webId}`);\n\n sendJson(response, 201, {\n success: true,\n nodeId: result.nodeId,\n token: result.token,\n displayName,\n createdAt: result.createdAt,\n message: 'Edge node created. Save the token - it will not be shown again.',\n });\n } catch (error) {\n logger.error(`Failed to create node: ${error}`);\n sendJson(response, 500, { error: 'Failed to create node' });\n }\n });\n\n // GET /api/nodes/:nodeId - Get node info\n server.get('/v1/nodes/:nodeId', async (request, response, params) => {\n if (rejectApiKey(request, response)) {\n return;\n }\n const auth = request.auth!;\n const webId = getWebId(auth);\n const nodeId = decodeURIComponent(params.nodeId);\n\n if (!webId) {\n sendJson(response, 400, { error: 'Cannot determine user' });\n return;\n }\n\n try {\n // Check ownership\n const nodeOwner = await repo.getNodeOwner(nodeId);\n if (nodeOwner !== webId) {\n sendJson(response, 403, { error: 'Access denied' });\n return;\n }\n\n const nodeInfo = await repo.getNodeCapabilities(nodeId);\n if (!nodeInfo) {\n sendJson(response, 404, { error: 'Node not found' });\n return;\n }\n\n sendJson(response, 200, formatNodeInfo(nodeInfo));\n } catch (error) {\n logger.error(`Failed to get node: ${error}`);\n sendJson(response, 500, { error: 'Failed to get node info' });\n }\n });\n\n // DELETE /api/nodes/:nodeId - Delete a node\n server.delete('/v1/nodes/:nodeId', async (request, response, params) => {\n if (rejectApiKey(request, response)) {\n return;\n }\n const auth = request.auth!;\n const webId = getWebId(auth);\n const nodeId = decodeURIComponent(params.nodeId);\n\n if (!webId) {\n sendJson(response, 400, { error: 'Cannot determine user' });\n return;\n }\n\n try {\n // Check ownership\n const nodeOwner = await repo.getNodeOwner(nodeId);\n if (nodeOwner !== webId) {\n sendJson(response, 403, { error: 'Access denied' });\n return;\n }\n\n const deleted = await repo.deleteNode(nodeId);\n if (!deleted) {\n sendJson(response, 404, { error: 'Node not found' });\n return;\n }\n\n logger.info(`Deleted node ${nodeId}`);\n sendJson(response, 200, { status: 'deleted', nodeId });\n } catch (error) {\n logger.error(`Failed to delete node: ${error}`);\n sendJson(response, 500, { error: 'Failed to delete node' });\n }\n });\n}\n\nfunction formatNodeInfo(node: any): Record<string, unknown> {\n return {\n nodeId: node.nodeId,\n displayName: node.displayName,\n accessMode: node.accessMode,\n connectivityStatus: node.connectivityStatus,\n lastSeen: node.lastSeen?.toISOString?.() ?? node.lastSeen,\n capabilities: {\n structured: node.capabilities,\n strings: node.stringCapabilities,\n },\n };\n}\n\nasync function readJsonBody(request: AuthenticatedRequest): Promise<unknown> {\n return new Promise((resolve, reject) => {\n let data = '';\n request.setEncoding('utf8');\n request.on('data', (chunk: string) => {\n data += chunk;\n });\n request.on('end', () => {\n if (!data) {\n resolve(undefined);\n return;\n }\n try {\n resolve(JSON.parse(data));\n } catch {\n resolve(undefined);\n }\n });\n request.on('error', reject);\n });\n}\n\nfunction sendJson(response: ServerResponse, status: number, data: unknown): void {\n response.statusCode = status;\n response.setHeader('Content-Type', 'application/json');\n response.end(JSON.stringify(data));\n}\n"]}
|
|
1
|
+
{"version":3,"file":"NodeHandler.js","sourceRoot":"","sources":["../../../src/api/handlers/NodeHandler.ts"],"names":[],"mappings":";;AAmBA,gDAiCC;AAnDD,iEAAqD;AAQrD;;;;;;;;;GASG;AACH,SAAgB,kBAAkB,CAAC,MAAiB,EAAE,OAA2B;IAC/E,MAAM,MAAM,GAAG,IAAA,oCAAY,EAAC,aAAa,CAAC,CAAC;IAC3C,KAAK,OAAO,CAAC;IAEb,MAAM,eAAe,GAAG,CAAC,QAAwB,EAAQ,EAAE;QACzD,QAAQ,CAAC,QAAQ,EAAE,GAAG,EAAE;YACtB,KAAK,EAAE,gDAAgD;SACxD,CAAC,CAAC;IACL,CAAC,CAAC;IAEF,oCAAoC;IACpC,MAAM,CAAC,GAAG,CAAC,WAAW,EAAE,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,OAAO,EAAE,EAAE;QAC5D,MAAM,CAAC,IAAI,CAAC,+DAA+D,CAAC,CAAC;QAC7E,eAAe,CAAC,QAAQ,CAAC,CAAC;IAC5B,CAAC,CAAC,CAAC;IAEH,sCAAsC;IACtC,MAAM,CAAC,IAAI,CAAC,WAAW,EAAE,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,OAAO,EAAE,EAAE;QAC7D,MAAM,CAAC,IAAI,CAAC,gEAAgE,CAAC,CAAC;QAC9E,eAAe,CAAC,QAAQ,CAAC,CAAC;IAC5B,CAAC,CAAC,CAAC;IAEH,yCAAyC;IACzC,MAAM,CAAC,GAAG,CAAC,mBAAmB,EAAE,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,OAAO,EAAE,EAAE;QACpE,MAAM,CAAC,IAAI,CAAC,uEAAuE,CAAC,CAAC;QACrF,eAAe,CAAC,QAAQ,CAAC,CAAC;IAC5B,CAAC,CAAC,CAAC;IAEH,4CAA4C;IAC5C,MAAM,CAAC,MAAM,CAAC,mBAAmB,EAAE,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,OAAO,EAAE,EAAE;QACvE,MAAM,CAAC,IAAI,CAAC,0EAA0E,CAAC,CAAC;QACxF,eAAe,CAAC,QAAQ,CAAC,CAAC;IAC5B,CAAC,CAAC,CAAC;AACL,CAAC;AAED,SAAS,QAAQ,CAAC,QAAwB,EAAE,MAAc,EAAE,IAAa;IACvE,QAAQ,CAAC,UAAU,GAAG,MAAM,CAAC;IAC7B,QAAQ,CAAC,SAAS,CAAC,cAAc,EAAE,kBAAkB,CAAC,CAAC;IACvD,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC;AACrC,CAAC","sourcesContent":["import type { ServerResponse } from 'node:http';\nimport { getLoggerFor } from 'global-logger-factory';\nimport type { ApiServer } from '../ApiServer';\nimport type { EdgeNodeRepository } from '../../identity/drizzle/EdgeNodeRepository';\n\nexport interface NodeHandlerOptions {\n repository: EdgeNodeRepository;\n}\n\n/**\n * Handler for edge node management API\n *\n * GET /v1/nodes - List user's nodes\n * POST /v1/nodes - Create a new node\n * GET /v1/nodes/:nodeId - Get node info\n * DELETE /v1/nodes/:nodeId - Delete a node\n *\n * All endpoints require Solid Token (user must have a Pod)\n */\nexport function registerNodeRoutes(server: ApiServer, options: NodeHandlerOptions): void {\n const logger = getLoggerFor('NodeHandler');\n void options;\n\n const sendUnavailable = (response: ServerResponse): void => {\n sendJson(response, 501, {\n error: 'Node management API is temporarily unavailable',\n });\n };\n\n // GET /v1/nodes - List user's nodes\n server.get('/v1/nodes', async (_request, response, _params) => {\n logger.info('Node management API is temporarily unavailable: GET /v1/nodes');\n sendUnavailable(response);\n });\n\n // POST /api/nodes - Create a new node\n server.post('/v1/nodes', async (_request, response, _params) => {\n logger.info('Node management API is temporarily unavailable: POST /v1/nodes');\n sendUnavailable(response);\n });\n\n // GET /api/nodes/:nodeId - Get node info\n server.get('/v1/nodes/:nodeId', async (_request, response, _params) => {\n logger.info('Node management API is temporarily unavailable: GET /v1/nodes/:nodeId');\n sendUnavailable(response);\n });\n\n // DELETE /api/nodes/:nodeId - Delete a node\n server.delete('/v1/nodes/:nodeId', async (_request, response, _params) => {\n logger.info('Node management API is temporarily unavailable: DELETE /v1/nodes/:nodeId');\n sendUnavailable(response);\n });\n}\n\nfunction sendJson(response: ServerResponse, status: number, data: unknown): void {\n response.statusCode = status;\n response.setHeader('Content-Type', 'application/json');\n response.end(JSON.stringify(data));\n}\n"]}
|
|
@@ -46,11 +46,11 @@ export declare class EdgeNodeRepository {
|
|
|
46
46
|
private readonly db;
|
|
47
47
|
constructor(db: IdentityDatabase);
|
|
48
48
|
listNodes(): Promise<EdgeNodeSummary[]>;
|
|
49
|
-
createNode(displayName?: string,
|
|
49
|
+
createNode(displayName?: string, _accountId?: string): Promise<CreateEdgeNodeResult>;
|
|
50
50
|
/**
|
|
51
|
-
*
|
|
51
|
+
* Node/account 关系待产品化后单独建模;当前阶段不再在节点表上持久化账号归属。
|
|
52
52
|
*/
|
|
53
|
-
getNodeOwner(
|
|
53
|
+
getNodeOwner(_nodeId: string): Promise<string | undefined>;
|
|
54
54
|
getNodeSecret(nodeId: string): Promise<EdgeNodeSecret | undefined>;
|
|
55
55
|
updateNodeHeartbeat(nodeId: string, metadata: Record<string, unknown> | null, timestamp: Date): Promise<void>;
|
|
56
56
|
updateNodeMode(nodeId: string, options: {
|
|
@@ -175,7 +175,6 @@ export declare class EdgeNodeRepository {
|
|
|
175
175
|
registerSpNode(options: {
|
|
176
176
|
publicUrl: string;
|
|
177
177
|
displayName?: string;
|
|
178
|
-
ownerAccountId?: string;
|
|
179
178
|
/** SP 提供的设备 ID,作为 nodeId(不传则随机生成) */
|
|
180
179
|
nodeId?: string;
|
|
181
180
|
/** SP 提供的 serviceToken,不传则随机生成 */
|
|
@@ -43,15 +43,15 @@ class EdgeNodeRepository {
|
|
|
43
43
|
};
|
|
44
44
|
});
|
|
45
45
|
}
|
|
46
|
-
async createNode(displayName,
|
|
46
|
+
async createNode(displayName, _accountId) {
|
|
47
47
|
const nodeId = (0, node_crypto_1.randomUUID)();
|
|
48
48
|
const token = (0, node_crypto_1.randomBytes)(32).toString('base64url');
|
|
49
49
|
const tokenHash = (0, node_crypto_1.createHash)('sha256').update(token).digest('hex');
|
|
50
50
|
const now = new Date();
|
|
51
51
|
const ts = (0, db_1.toDbTimestamp)(this.db, now);
|
|
52
52
|
await (0, db_1.executeStatement)(this.db, (0, drizzle_orm_1.sql) `
|
|
53
|
-
INSERT INTO identity_edge_node (id, display_name,
|
|
54
|
-
VALUES (${nodeId}, ${displayName ?? null}, ${
|
|
53
|
+
INSERT INTO identity_edge_node (id, display_name, token_hash, created_at, updated_at)
|
|
54
|
+
VALUES (${nodeId}, ${displayName ?? null}, ${tokenHash}, ${ts}, ${ts})
|
|
55
55
|
`);
|
|
56
56
|
return {
|
|
57
57
|
nodeId,
|
|
@@ -60,19 +60,10 @@ class EdgeNodeRepository {
|
|
|
60
60
|
};
|
|
61
61
|
}
|
|
62
62
|
/**
|
|
63
|
-
*
|
|
63
|
+
* Node/account 关系待产品化后单独建模;当前阶段不再在节点表上持久化账号归属。
|
|
64
64
|
*/
|
|
65
|
-
async getNodeOwner(
|
|
66
|
-
|
|
67
|
-
SELECT owner_account_id
|
|
68
|
-
FROM identity_edge_node
|
|
69
|
-
WHERE id = ${nodeId}
|
|
70
|
-
LIMIT 1
|
|
71
|
-
`);
|
|
72
|
-
if (result.rows.length === 0) {
|
|
73
|
-
return undefined;
|
|
74
|
-
}
|
|
75
|
-
return result.rows[0].owner_account_id ? String(result.rows[0].owner_account_id) : undefined;
|
|
65
|
+
async getNodeOwner(_nodeId) {
|
|
66
|
+
return undefined;
|
|
76
67
|
}
|
|
77
68
|
async getNodeSecret(nodeId) {
|
|
78
69
|
const result = await (0, db_1.executeQuery)(this.db, (0, drizzle_orm_1.sql) `
|
|
@@ -444,25 +435,8 @@ class EdgeNodeRepository {
|
|
|
444
435
|
* List nodes owned by a specific account
|
|
445
436
|
*/
|
|
446
437
|
async listNodesByAccount(accountId) {
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
FROM identity_edge_node
|
|
450
|
-
WHERE owner_account_id = ${accountId} AND node_type = 'edge'
|
|
451
|
-
ORDER BY created_at DESC
|
|
452
|
-
`);
|
|
453
|
-
return result.rows.map((row) => {
|
|
454
|
-
const metadata = typeof row.metadata === 'string' ? JSON.parse(row.metadata) : row.metadata;
|
|
455
|
-
const capabilities = typeof row.capabilities === 'string' ? JSON.parse(row.capabilities) : row.capabilities;
|
|
456
|
-
return {
|
|
457
|
-
nodeId: String(row.id),
|
|
458
|
-
displayName: row.display_name ?? undefined,
|
|
459
|
-
capabilities: capabilities ?? null,
|
|
460
|
-
stringCapabilities: metadata?.capabilities ?? null,
|
|
461
|
-
accessMode: row.access_mode ?? null,
|
|
462
|
-
lastSeen: (0, db_1.fromDbTimestamp)(row.last_seen) ?? null,
|
|
463
|
-
connectivityStatus: row.connectivity_status ?? null,
|
|
464
|
-
};
|
|
465
|
-
});
|
|
438
|
+
void accountId;
|
|
439
|
+
return [];
|
|
466
440
|
}
|
|
467
441
|
/**
|
|
468
442
|
* Delete a node
|
|
@@ -497,13 +471,12 @@ class EdgeNodeRepository {
|
|
|
497
471
|
const ts = (0, db_1.toDbTimestamp)(this.db, now);
|
|
498
472
|
await (0, db_1.executeStatement)(this.db, (0, drizzle_orm_1.sql) `
|
|
499
473
|
INSERT INTO identity_edge_node (
|
|
500
|
-
id, display_name,
|
|
474
|
+
id, display_name, token_hash, service_token_hash,
|
|
501
475
|
node_type, public_url,
|
|
502
476
|
connectivity_status, created_at, updated_at
|
|
503
477
|
)
|
|
504
478
|
VALUES (
|
|
505
|
-
${nodeId}, ${options.displayName ?? null}, ${
|
|
506
|
-
${nodeTokenHash}, ${serviceToken},
|
|
479
|
+
${nodeId}, ${options.displayName ?? null}, ${nodeTokenHash}, ${serviceToken},
|
|
507
480
|
'sp', ${options.publicUrl}, 'unknown', ${ts}, ${ts}
|
|
508
481
|
)
|
|
509
482
|
ON CONFLICT (id) DO UPDATE SET
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EdgeNodeRepository.js","sourceRoot":"","sources":["../../../src/identity/drizzle/EdgeNodeRepository.ts"],"names":[],"mappings":"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","sourcesContent":["import { randomBytes, randomUUID, createHash, timingSafeEqual } from 'node:crypto';\nimport { sql, eq } from 'drizzle-orm';\nimport type { IdentityDatabase } from './db';\nimport { executeStatement, executeQuery, toDbTimestamp, fromDbTimestamp } from './db';\nimport { edgeNodes } from './schema';\n\nexport interface EdgeNodeSummary {\n nodeId: string;\n displayName?: string;\n nodeType: 'center' | 'edge' | 'sp';\n podCount: number;\n createdAt?: string;\n updatedAt?: string;\n lastSeen?: string;\n metadata?: Record<string, unknown> | null;\n}\n\nexport interface CreateEdgeNodeResult {\n nodeId: string;\n token: string;\n createdAt: string;\n}\n\nexport interface EdgeNodeSecret {\n nodeId: string;\n displayName?: string;\n tokenHash: string;\n nodeType: 'center' | 'edge' | 'sp';\n metadata?: Record<string, unknown> | null;\n}\n\nexport interface CenterNodeInfo {\n nodeId: string;\n displayName?: string;\n internalIp: string;\n internalPort: number;\n connectivityStatus: 'unknown' | 'reachable' | 'unreachable';\n lastSeen?: Date;\n}\n\nexport interface CreateSpNodeResult {\n nodeId: string;\n nodeToken: string;\n serviceToken: string;\n createdAt: string;\n}\n\nexport interface SpNodeInfo {\n nodeId: string;\n displayName?: string;\n publicUrl: string;\n serviceTokenHash: string;\n lastSeen?: Date;\n}\n\nexport class EdgeNodeRepository {\n public constructor(private readonly db: IdentityDatabase) {}\n\n public async listNodes(): Promise<EdgeNodeSummary[]> {\n const result = await executeQuery(this.db, sql`\n SELECT en.id,\n en.display_name,\n en.node_type,\n en.created_at,\n en.updated_at,\n en.last_seen,\n en.metadata,\n COALESCE(pods.count, 0) AS pod_count\n FROM identity_edge_node en\n LEFT JOIN (\n SELECT node_id, COUNT(*) AS count\n FROM identity_edge_node_pod\n GROUP BY node_id\n ) pods ON pods.node_id = en.id\n ORDER BY en.created_at ASC\n `);\n\n return result.rows.map((row: any): EdgeNodeSummary => {\n const createdAt = fromDbTimestamp(row.created_at);\n const updatedAt = fromDbTimestamp(row.updated_at);\n const lastSeen = fromDbTimestamp(row.last_seen);\n return {\n nodeId: String(row.id),\n displayName: row.display_name == null ? undefined : String(row.display_name),\n nodeType: (['center', 'edge', 'sp'].includes(row.node_type) ? row.node_type : 'edge') as 'center' | 'edge' | 'sp',\n podCount: Number(row.pod_count ?? 0),\n createdAt: createdAt?.toISOString(),\n updatedAt: updatedAt?.toISOString(),\n lastSeen: lastSeen?.toISOString(),\n metadata: typeof row.metadata === 'string' ? JSON.parse(row.metadata) : (row.metadata ?? null),\n };\n });\n }\n\n public async createNode(displayName?: string, accountId?: string): Promise<CreateEdgeNodeResult> {\n const nodeId = randomUUID();\n const token = randomBytes(32).toString('base64url');\n const tokenHash = createHash('sha256').update(token).digest('hex');\n const now = new Date();\n const ts = toDbTimestamp(this.db, now);\n\n await executeStatement(this.db, sql`\n INSERT INTO identity_edge_node (id, display_name, owner_account_id, token_hash, created_at, updated_at)\n VALUES (${nodeId}, ${displayName ?? null}, ${accountId ?? null}, ${tokenHash}, ${ts}, ${ts})\n `);\n\n return {\n nodeId,\n token,\n createdAt: now.toISOString(),\n };\n }\n\n /**\n * Get the owner account ID of a node.\n */\n public async getNodeOwner(nodeId: string): Promise<string | undefined> {\n const result = await executeQuery(this.db, sql`\n SELECT owner_account_id\n FROM identity_edge_node\n WHERE id = ${nodeId}\n LIMIT 1\n `);\n \n if (result.rows.length === 0) {\n return undefined;\n }\n \n return result.rows[0].owner_account_id ? String(result.rows[0].owner_account_id) : undefined;\n }\n\n public async getNodeSecret(nodeId: string): Promise<EdgeNodeSecret | undefined> {\n const result = await executeQuery(this.db, sql`\n SELECT id, display_name, token_hash, node_type, metadata\n FROM identity_edge_node\n WHERE id = ${nodeId}\n LIMIT 1\n `);\n if (result.rows.length === 0) {\n return undefined;\n }\n const row = result.rows[0] as any;\n return {\n nodeId: String(row.id),\n displayName: row.display_name == null ? undefined : String(row.display_name),\n tokenHash: String(row.token_hash ?? ''),\n nodeType: (['center', 'edge', 'sp'].includes(row.node_type) ? row.node_type : 'edge') as 'center' | 'edge' | 'sp',\n metadata: typeof row.metadata === 'string' ? JSON.parse(row.metadata) : (row.metadata ?? null),\n };\n }\n\n public async updateNodeHeartbeat(nodeId: string, metadata: Record<string, unknown> | null, timestamp: Date): Promise<void> {\n const payload = metadata == null ? null : JSON.stringify(metadata);\n const ts = toDbTimestamp(this.db, timestamp);\n\n await executeStatement(this.db, sql`\n UPDATE identity_edge_node\n SET metadata = ${payload},\n last_seen = ${ts},\n updated_at = ${ts}\n WHERE id = ${nodeId}\n `);\n }\n\n public async updateNodeMode(nodeId: string, options: {\n accessMode: 'direct' | 'proxy';\n ipv4?: string;\n publicPort?: number;\n subdomain?: string;\n connectivityStatus?: 'unknown' | 'reachable' | 'unreachable';\n capabilities?: Record<string, unknown>;\n }): Promise<void> {\n const capabilitiesPayload = options.capabilities ? JSON.stringify(options.capabilities) : null;\n const now = new Date();\n const ts = toDbTimestamp(this.db, now);\n\n await executeStatement(this.db, sql`\n UPDATE identity_edge_node\n SET access_mode = ${options.accessMode},\n ipv4 = ${options.ipv4 ?? null},\n public_port = ${options.publicPort ?? null},\n subdomain = ${options.subdomain ?? null},\n connectivity_status = ${options.connectivityStatus ?? 'unknown'},\n capabilities = ${capabilitiesPayload},\n last_connectivity_check = ${ts},\n updated_at = ${ts}\n WHERE id = ${nodeId}\n `);\n }\n\n public async getNodeConnectivityInfo(nodeId: string): Promise<{\n nodeId: string;\n accessMode?: string;\n ipv4?: string;\n publicPort?: number;\n subdomain?: string;\n connectivityStatus?: string;\n lastConnectivityCheck?: Date;\n } | undefined> {\n const result = await executeQuery(this.db, sql`\n SELECT id, access_mode, ipv4, public_port, subdomain,\n connectivity_status, last_connectivity_check\n FROM identity_edge_node\n WHERE id = ${nodeId}\n LIMIT 1\n `);\n\n if (result.rows.length === 0) {\n return undefined;\n }\n\n const row = result.rows[0] as any;\n return {\n nodeId: String(row.id),\n accessMode: row.access_mode ? String(row.access_mode) : undefined,\n ipv4: row.ipv4 ? String(row.ipv4) : undefined,\n publicPort: row.public_port ? Number(row.public_port) : undefined,\n subdomain: row.subdomain ? String(row.subdomain) : undefined,\n connectivityStatus: row.connectivity_status ? String(row.connectivity_status) : undefined,\n lastConnectivityCheck: fromDbTimestamp(row.last_connectivity_check),\n };\n }\n\n public async mergeNodeMetadata(nodeId: string, patch: Record<string, unknown>): Promise<void> {\n // Read current metadata\n const current = await this.getNodeMetadata(nodeId);\n if (!current) {\n throw new Error(`Node ${nodeId} not found`);\n }\n\n // Merge in application layer\n const merged = { ...(current.metadata ?? {}), ...patch };\n const payload = JSON.stringify(merged);\n const ts = toDbTimestamp(this.db, new Date());\n\n await executeStatement(this.db, sql`\n UPDATE identity_edge_node\n SET metadata = ${payload},\n updated_at = ${ts}\n WHERE id = ${nodeId}\n `);\n }\n\n public async getNodeMetadata(nodeId: string): Promise<{ nodeId: string; metadata: Record<string, unknown> | null; lastSeen?: Date } | undefined> {\n const result = await executeQuery(this.db, sql`\n SELECT id, metadata, last_seen\n FROM identity_edge_node\n WHERE id = ${nodeId}\n LIMIT 1\n `);\n if (result.rows.length === 0) {\n return undefined;\n }\n const row = result.rows[0] as any;\n return {\n nodeId: String(row.id),\n metadata: typeof row.metadata === 'string' ? JSON.parse(row.metadata) : (row.metadata ?? null),\n lastSeen: fromDbTimestamp(row.last_seen),\n };\n }\n\n public async replaceNodePods(nodeId: string, pods: string[]): Promise<void> {\n await this.db.transaction(async (tx: IdentityDatabase) => {\n await tx.execute(sql`DELETE FROM identity_edge_node_pod WHERE node_id = ${nodeId}`);\n if (pods.length > 0) {\n const values = pods.map((baseUrl) => sql`(${nodeId}, ${baseUrl})`);\n await tx.execute(sql`\n INSERT INTO identity_edge_node_pod (node_id, base_url)\n VALUES ${sql.join(values, sql`, `)}\n ON CONFLICT DO NOTHING\n `);\n }\n });\n }\n\n public async findNodeByResourcePath(path: string): Promise<{ nodeId: string; baseUrl: string; accessMode?: string; metadata?: Record<string, unknown> | null } | undefined> {\n const result = await executeQuery(this.db, sql`\n SELECT en.id,\n en.access_mode,\n en.metadata,\n pods.base_url\n FROM identity_edge_node_pod pods\n JOIN identity_edge_node en ON en.id = pods.node_id\n WHERE ${path} LIKE pods.base_url || '%'\n ORDER BY length(pods.base_url) DESC\n LIMIT 1\n `);\n if (result.rows.length === 0) {\n return undefined;\n }\n const row = result.rows[0] as any;\n return {\n nodeId: String(row.id),\n baseUrl: String(row.base_url),\n accessMode: row.access_mode ? String(row.access_mode) : undefined,\n metadata: row.metadata ?? null,\n };\n }\n\n public async findNodeBySubdomain(hostname: string): Promise<{ nodeId: string; accessMode?: string; metadata?: Record<string, unknown> | null; subdomain?: string } | undefined> {\n const normalized = hostname.trim().toLowerCase();\n if (normalized.length === 0) {\n return undefined;\n }\n const result = await executeQuery(this.db, sql`\n SELECT id, access_mode, metadata, subdomain\n FROM identity_edge_node\n WHERE subdomain = ${normalized}\n LIMIT 1\n `);\n if (result.rows.length === 0) {\n return undefined;\n }\n const row = result.rows[0] as any;\n return {\n nodeId: String(row.id),\n accessMode: row.access_mode ? String(row.access_mode) : undefined,\n metadata: row.metadata ?? null,\n subdomain: row.subdomain ? String(row.subdomain) : undefined,\n };\n }\n\n public matchesToken(tokenHash: string, token: string): boolean {\n if (!tokenHash || typeof tokenHash !== 'string') {\n return false;\n }\n try {\n const expected = Buffer.from(tokenHash, 'hex');\n const actual = createHash('sha256').update(token).digest();\n if (expected.length !== actual.length) {\n return false;\n }\n return timingSafeEqual(expected, actual);\n } catch {\n return false;\n }\n }\n\n /**\n * Get node capabilities and related information for admin queries\n */\n public async getNodeCapabilities(nodeId: string): Promise<{\n nodeId: string;\n capabilities: Record<string, unknown> | null;\n stringCapabilities: string[] | null;\n accessMode: string | null;\n lastSeen: Date | null;\n connectivityStatus: string | null;\n } | undefined> {\n const row = await this.db\n .select({\n id: edgeNodes.id,\n capabilities: edgeNodes.capabilities,\n metadata: edgeNodes.metadata,\n accessMode: edgeNodes.accessMode,\n lastSeen: edgeNodes.lastSeen,\n connectivityStatus: edgeNodes.connectivityStatus,\n })\n .from(edgeNodes)\n .where(eq(edgeNodes.id, nodeId))\n .limit(1);\n\n if (row.length === 0) {\n return undefined;\n }\n\n const node = row[0];\n const metadata = node.metadata as Record<string, unknown> | null;\n \n return {\n nodeId: node.id,\n capabilities: node.capabilities as Record<string, unknown> | null,\n stringCapabilities: metadata?.capabilities as string[] ?? null,\n accessMode: node.accessMode,\n lastSeen: node.lastSeen,\n connectivityStatus: node.connectivityStatus,\n };\n }\n\n /**\n * List all nodes with their capability information\n */\n public async listNodeCapabilities(): Promise<Array<{\n nodeId: string;\n capabilities: Record<string, unknown> | null;\n stringCapabilities: string[] | null;\n accessMode: string | null;\n lastSeen: Date | null;\n connectivityStatus: string | null;\n }>> {\n const rows = await this.db\n .select({\n id: edgeNodes.id,\n capabilities: edgeNodes.capabilities,\n metadata: edgeNodes.metadata,\n accessMode: edgeNodes.accessMode,\n lastSeen: edgeNodes.lastSeen,\n connectivityStatus: edgeNodes.connectivityStatus,\n })\n .from(edgeNodes)\n .orderBy(edgeNodes.lastSeen);\n\n return rows.map((row: typeof rows[0]) => {\n const metadata = row.metadata as Record<string, unknown> | null;\n \n return {\n nodeId: row.id,\n capabilities: row.capabilities as Record<string, unknown> | null,\n stringCapabilities: metadata?.capabilities as string[] ?? null,\n accessMode: row.accessMode,\n lastSeen: row.lastSeen,\n connectivityStatus: row.connectivityStatus,\n };\n });\n }\n\n // ============ Center Node Methods ============\n\n /**\n * Register or update a center node in the cluster.\n * Center nodes use the same table as edge nodes but with nodeType='center'.\n */\n public async registerCenterNode(options: {\n nodeId: string;\n displayName?: string;\n internalIp: string;\n internalPort: number;\n }): Promise<{ nodeId: string; token: string }> {\n const token = randomBytes(32).toString('base64url');\n const tokenHash = createHash('sha256').update(token).digest('hex');\n const now = Math.floor(Date.now() / 1000); // Unix timestamp for SQLite compatibility\n\n // Use upsert pattern: INSERT ... ON CONFLICT UPDATE\n await executeStatement(this.db, sql`\n INSERT INTO identity_edge_node (\n id, display_name, token_hash, node_type, internal_ip, internal_port,\n connectivity_status, created_at, updated_at, last_seen\n )\n VALUES (\n ${options.nodeId}, ${options.displayName ?? null}, ${tokenHash}, 'center',\n ${options.internalIp}, ${options.internalPort}, 'unknown', ${now}, ${now}, ${now}\n )\n ON CONFLICT (id) DO UPDATE SET\n display_name = EXCLUDED.display_name,\n internal_ip = EXCLUDED.internal_ip,\n internal_port = EXCLUDED.internal_port,\n updated_at = EXCLUDED.updated_at,\n last_seen = EXCLUDED.last_seen\n `);\n\n return { nodeId: options.nodeId, token };\n }\n\n /**\n * Update center node heartbeat with internal endpoint info.\n */\n public async updateCenterNodeHeartbeat(\n nodeId: string,\n internalIp: string,\n internalPort: number,\n timestamp: Date,\n ): Promise<void> {\n const ts = Math.floor(timestamp.getTime() / 1000); // Unix timestamp for SQLite compatibility\n await executeStatement(this.db, sql`\n UPDATE identity_edge_node\n SET internal_ip = ${internalIp},\n internal_port = ${internalPort},\n last_seen = ${ts},\n updated_at = ${ts},\n connectivity_status = 'reachable'\n WHERE id = ${nodeId} AND node_type = 'center'\n `);\n }\n\n /**\n * List all center nodes in the cluster.\n */\n public async listCenterNodes(): Promise<CenterNodeInfo[]> {\n const result = await executeQuery(this.db, sql`\n SELECT id, display_name, internal_ip, internal_port, connectivity_status, last_seen\n FROM identity_edge_node\n WHERE node_type = 'center'\n ORDER BY created_at ASC\n `);\n\n return result.rows.map((row: any): CenterNodeInfo => ({\n nodeId: String(row.id),\n displayName: row.display_name == null ? undefined : String(row.display_name),\n internalIp: String(row.internal_ip ?? ''),\n internalPort: Number(row.internal_port ?? 0),\n connectivityStatus: (row.connectivity_status ?? 'unknown') as 'unknown' | 'reachable' | 'unreachable',\n lastSeen: fromDbTimestamp(row.last_seen),\n }));\n }\n\n /**\n * Get a specific center node by ID.\n */\n public async getCenterNode(nodeId: string): Promise<CenterNodeInfo | undefined> {\n const result = await executeQuery(this.db, sql`\n SELECT id, display_name, internal_ip, internal_port, connectivity_status, last_seen\n FROM identity_edge_node\n WHERE id = ${nodeId} AND node_type = 'center'\n LIMIT 1\n `);\n\n if (result.rows.length === 0) {\n return undefined;\n }\n\n const row = result.rows[0] as any;\n return {\n nodeId: String(row.id),\n displayName: row.display_name == null ? undefined : String(row.display_name),\n internalIp: String(row.internal_ip ?? ''),\n internalPort: Number(row.internal_port ?? 0),\n connectivityStatus: (row.connectivity_status ?? 'unknown') as 'unknown' | 'reachable' | 'unreachable',\n lastSeen: fromDbTimestamp(row.last_seen),\n };\n }\n\n /**\n * Find a center node by its internal endpoint (for routing).\n */\n public async findCenterNodeByEndpoint(internalIp: string, internalPort: number): Promise<CenterNodeInfo | undefined> {\n const result = await executeQuery(this.db, sql`\n SELECT id, display_name, internal_ip, internal_port, connectivity_status, last_seen\n FROM identity_edge_node\n WHERE node_type = 'center' AND internal_ip = ${internalIp} AND internal_port = ${internalPort}\n LIMIT 1\n `);\n\n if (result.rows.length === 0) {\n return undefined;\n }\n\n const row = result.rows[0] as any;\n return {\n nodeId: String(row.id),\n displayName: row.display_name == null ? undefined : String(row.display_name),\n internalIp: String(row.internal_ip ?? ''),\n internalPort: Number(row.internal_port ?? 0),\n connectivityStatus: (row.connectivity_status ?? 'unknown') as 'unknown' | 'reachable' | 'unreachable',\n lastSeen: fromDbTimestamp(row.last_seen),\n };\n }\n\n /**\n * Mark a center node as unreachable (for health checks).\n */\n public async markCenterNodeUnreachable(nodeId: string): Promise<void> {\n const ts = toDbTimestamp(this.db, new Date());\n await executeStatement(this.db, sql`\n UPDATE identity_edge_node\n SET connectivity_status = 'unreachable',\n updated_at = ${ts}\n WHERE id = ${nodeId} AND node_type = 'center'\n `);\n }\n\n /**\n * Remove a center node from the cluster.\n */\n public async removeCenterNode(nodeId: string): Promise<boolean> {\n // Note: For SQLite, we can't easily get affected row count, so just execute and return true\n await executeStatement(this.db, sql`\n DELETE FROM identity_edge_node\n WHERE id = ${nodeId} AND node_type = 'center'\n `);\n return true;\n }\n\n // ============ Account-based Node Methods ============\n\n /**\n * List nodes owned by a specific account\n */\n public async listNodesByAccount(accountId: string): Promise<Array<{\n nodeId: string;\n displayName?: string;\n capabilities: Record<string, unknown> | null;\n stringCapabilities: string[] | null;\n accessMode: string | null;\n lastSeen: Date | null;\n connectivityStatus: string | null;\n }>> {\n const result = await executeQuery(this.db, sql`\n SELECT id, display_name, capabilities, metadata, access_mode, last_seen, connectivity_status\n FROM identity_edge_node\n WHERE owner_account_id = ${accountId} AND node_type = 'edge'\n ORDER BY created_at DESC\n `);\n\n return result.rows.map((row: any) => {\n const metadata = typeof row.metadata === 'string' ? JSON.parse(row.metadata) : (row.metadata as Record<string, unknown> | null);\n const capabilities = typeof row.capabilities === 'string' ? JSON.parse(row.capabilities) : (row.capabilities as Record<string, unknown> | null);\n return {\n nodeId: String(row.id),\n displayName: row.display_name ?? undefined,\n capabilities: capabilities ?? null,\n stringCapabilities: (metadata?.capabilities as string[] | undefined) ?? null,\n accessMode: row.access_mode ?? null,\n lastSeen: fromDbTimestamp(row.last_seen) ?? null,\n connectivityStatus: row.connectivity_status ?? null,\n };\n });\n }\n\n /**\n * Delete a node\n */\n public async deleteNode(nodeId: string): Promise<boolean> {\n // First delete associated pods\n await executeStatement(this.db, sql`\n DELETE FROM identity_edge_node_pod WHERE node_id = ${nodeId}\n `);\n\n // Then delete the node\n const result = await executeQuery(this.db, sql`\n DELETE FROM identity_edge_node\n WHERE id = ${nodeId}\n RETURNING id\n `);\n\n return result.rows.length > 0;\n }\n\n // ============ SP (Storage Provider) Node Methods ============\n\n /**\n * Register or update an SP node (UPSERT by nodeId).\n *\n * SP 本地生成 deviceId 作为 nodeId,注册时带上来。\n * 同一 nodeId 重复注册时更新 publicUrl、token 等,保留原记录。\n * 不传 nodeId 则 Cloud 随机分配。\n */\n public async registerSpNode(options: {\n publicUrl: string;\n displayName?: string;\n ownerAccountId?: string;\n /** SP 提供的设备 ID,作为 nodeId(不传则随机生成) */\n nodeId?: string;\n /** SP 提供的 serviceToken,不传则随机生成 */\n serviceToken?: string;\n }): Promise<CreateSpNodeResult> {\n const nodeId = options.nodeId || randomUUID();\n const nodeToken = randomBytes(32).toString('base64url');\n const nodeTokenHash = createHash('sha256').update(nodeToken).digest('hex');\n const serviceToken = options.serviceToken || randomBytes(32).toString('base64url');\n const now = new Date();\n const ts = toDbTimestamp(this.db, now);\n\n await executeStatement(this.db, sql`\n INSERT INTO identity_edge_node (\n id, display_name, owner_account_id, token_hash, service_token_hash,\n node_type, public_url,\n connectivity_status, created_at, updated_at\n )\n VALUES (\n ${nodeId}, ${options.displayName ?? null}, ${options.ownerAccountId ?? null},\n ${nodeTokenHash}, ${serviceToken},\n 'sp', ${options.publicUrl}, 'unknown', ${ts}, ${ts}\n )\n ON CONFLICT (id) DO UPDATE SET\n display_name = EXCLUDED.display_name,\n token_hash = EXCLUDED.token_hash,\n service_token_hash = EXCLUDED.service_token_hash,\n public_url = EXCLUDED.public_url,\n updated_at = EXCLUDED.updated_at\n `);\n\n return {\n nodeId,\n nodeToken,\n serviceToken,\n createdAt: now.toISOString(),\n };\n }\n\n /**\n * Get SP node info by nodeId.\n */\n public async getSpNode(nodeId: string): Promise<SpNodeInfo | undefined> {\n const result = await executeQuery(this.db, sql`\n SELECT id, display_name, public_url, service_token_hash, last_seen\n FROM identity_edge_node\n WHERE id = ${nodeId} AND node_type = 'sp'\n LIMIT 1\n `);\n\n if (result.rows.length === 0) {\n return undefined;\n }\n\n const row = result.rows[0] as any;\n return {\n nodeId: String(row.id),\n displayName: row.display_name == null ? undefined : String(row.display_name),\n publicUrl: String(row.public_url ?? ''),\n serviceTokenHash: String(row.service_token_hash ?? ''),\n lastSeen: fromDbTimestamp(row.last_seen),\n };\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"EdgeNodeRepository.js","sourceRoot":"","sources":["../../../src/identity/drizzle/EdgeNodeRepository.ts"],"names":[],"mappings":"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","sourcesContent":["import { randomBytes, randomUUID, createHash, timingSafeEqual } from 'node:crypto';\nimport { sql, eq } from 'drizzle-orm';\nimport type { IdentityDatabase } from './db';\nimport { executeStatement, executeQuery, toDbTimestamp, fromDbTimestamp } from './db';\nimport { edgeNodes } from './schema';\n\nexport interface EdgeNodeSummary {\n nodeId: string;\n displayName?: string;\n nodeType: 'center' | 'edge' | 'sp';\n podCount: number;\n createdAt?: string;\n updatedAt?: string;\n lastSeen?: string;\n metadata?: Record<string, unknown> | null;\n}\n\nexport interface CreateEdgeNodeResult {\n nodeId: string;\n token: string;\n createdAt: string;\n}\n\nexport interface EdgeNodeSecret {\n nodeId: string;\n displayName?: string;\n tokenHash: string;\n nodeType: 'center' | 'edge' | 'sp';\n metadata?: Record<string, unknown> | null;\n}\n\nexport interface CenterNodeInfo {\n nodeId: string;\n displayName?: string;\n internalIp: string;\n internalPort: number;\n connectivityStatus: 'unknown' | 'reachable' | 'unreachable';\n lastSeen?: Date;\n}\n\nexport interface CreateSpNodeResult {\n nodeId: string;\n nodeToken: string;\n serviceToken: string;\n createdAt: string;\n}\n\nexport interface SpNodeInfo {\n nodeId: string;\n displayName?: string;\n publicUrl: string;\n serviceTokenHash: string;\n lastSeen?: Date;\n}\n\nexport class EdgeNodeRepository {\n public constructor(private readonly db: IdentityDatabase) {}\n\n public async listNodes(): Promise<EdgeNodeSummary[]> {\n const result = await executeQuery(this.db, sql`\n SELECT en.id,\n en.display_name,\n en.node_type,\n en.created_at,\n en.updated_at,\n en.last_seen,\n en.metadata,\n COALESCE(pods.count, 0) AS pod_count\n FROM identity_edge_node en\n LEFT JOIN (\n SELECT node_id, COUNT(*) AS count\n FROM identity_edge_node_pod\n GROUP BY node_id\n ) pods ON pods.node_id = en.id\n ORDER BY en.created_at ASC\n `);\n\n return result.rows.map((row: any): EdgeNodeSummary => {\n const createdAt = fromDbTimestamp(row.created_at);\n const updatedAt = fromDbTimestamp(row.updated_at);\n const lastSeen = fromDbTimestamp(row.last_seen);\n return {\n nodeId: String(row.id),\n displayName: row.display_name == null ? undefined : String(row.display_name),\n nodeType: (['center', 'edge', 'sp'].includes(row.node_type) ? row.node_type : 'edge') as 'center' | 'edge' | 'sp',\n podCount: Number(row.pod_count ?? 0),\n createdAt: createdAt?.toISOString(),\n updatedAt: updatedAt?.toISOString(),\n lastSeen: lastSeen?.toISOString(),\n metadata: typeof row.metadata === 'string' ? JSON.parse(row.metadata) : (row.metadata ?? null),\n };\n });\n }\n\n public async createNode(displayName?: string, _accountId?: string): Promise<CreateEdgeNodeResult> {\n const nodeId = randomUUID();\n const token = randomBytes(32).toString('base64url');\n const tokenHash = createHash('sha256').update(token).digest('hex');\n const now = new Date();\n const ts = toDbTimestamp(this.db, now);\n\n await executeStatement(this.db, sql`\n INSERT INTO identity_edge_node (id, display_name, token_hash, created_at, updated_at)\n VALUES (${nodeId}, ${displayName ?? null}, ${tokenHash}, ${ts}, ${ts})\n `);\n\n return {\n nodeId,\n token,\n createdAt: now.toISOString(),\n };\n }\n\n /**\n * Node/account 关系待产品化后单独建模;当前阶段不再在节点表上持久化账号归属。\n */\n public async getNodeOwner(_nodeId: string): Promise<string | undefined> {\n return undefined;\n }\n\n public async getNodeSecret(nodeId: string): Promise<EdgeNodeSecret | undefined> {\n const result = await executeQuery(this.db, sql`\n SELECT id, display_name, token_hash, node_type, metadata\n FROM identity_edge_node\n WHERE id = ${nodeId}\n LIMIT 1\n `);\n if (result.rows.length === 0) {\n return undefined;\n }\n const row = result.rows[0] as any;\n return {\n nodeId: String(row.id),\n displayName: row.display_name == null ? undefined : String(row.display_name),\n tokenHash: String(row.token_hash ?? ''),\n nodeType: (['center', 'edge', 'sp'].includes(row.node_type) ? row.node_type : 'edge') as 'center' | 'edge' | 'sp',\n metadata: typeof row.metadata === 'string' ? JSON.parse(row.metadata) : (row.metadata ?? null),\n };\n }\n\n public async updateNodeHeartbeat(nodeId: string, metadata: Record<string, unknown> | null, timestamp: Date): Promise<void> {\n const payload = metadata == null ? null : JSON.stringify(metadata);\n const ts = toDbTimestamp(this.db, timestamp);\n\n await executeStatement(this.db, sql`\n UPDATE identity_edge_node\n SET metadata = ${payload},\n last_seen = ${ts},\n updated_at = ${ts}\n WHERE id = ${nodeId}\n `);\n }\n\n public async updateNodeMode(nodeId: string, options: {\n accessMode: 'direct' | 'proxy';\n ipv4?: string;\n publicPort?: number;\n subdomain?: string;\n connectivityStatus?: 'unknown' | 'reachable' | 'unreachable';\n capabilities?: Record<string, unknown>;\n }): Promise<void> {\n const capabilitiesPayload = options.capabilities ? JSON.stringify(options.capabilities) : null;\n const now = new Date();\n const ts = toDbTimestamp(this.db, now);\n\n await executeStatement(this.db, sql`\n UPDATE identity_edge_node\n SET access_mode = ${options.accessMode},\n ipv4 = ${options.ipv4 ?? null},\n public_port = ${options.publicPort ?? null},\n subdomain = ${options.subdomain ?? null},\n connectivity_status = ${options.connectivityStatus ?? 'unknown'},\n capabilities = ${capabilitiesPayload},\n last_connectivity_check = ${ts},\n updated_at = ${ts}\n WHERE id = ${nodeId}\n `);\n }\n\n public async getNodeConnectivityInfo(nodeId: string): Promise<{\n nodeId: string;\n accessMode?: string;\n ipv4?: string;\n publicPort?: number;\n subdomain?: string;\n connectivityStatus?: string;\n lastConnectivityCheck?: Date;\n } | undefined> {\n const result = await executeQuery(this.db, sql`\n SELECT id, access_mode, ipv4, public_port, subdomain,\n connectivity_status, last_connectivity_check\n FROM identity_edge_node\n WHERE id = ${nodeId}\n LIMIT 1\n `);\n\n if (result.rows.length === 0) {\n return undefined;\n }\n\n const row = result.rows[0] as any;\n return {\n nodeId: String(row.id),\n accessMode: row.access_mode ? String(row.access_mode) : undefined,\n ipv4: row.ipv4 ? String(row.ipv4) : undefined,\n publicPort: row.public_port ? Number(row.public_port) : undefined,\n subdomain: row.subdomain ? String(row.subdomain) : undefined,\n connectivityStatus: row.connectivity_status ? String(row.connectivity_status) : undefined,\n lastConnectivityCheck: fromDbTimestamp(row.last_connectivity_check),\n };\n }\n\n public async mergeNodeMetadata(nodeId: string, patch: Record<string, unknown>): Promise<void> {\n // Read current metadata\n const current = await this.getNodeMetadata(nodeId);\n if (!current) {\n throw new Error(`Node ${nodeId} not found`);\n }\n\n // Merge in application layer\n const merged = { ...(current.metadata ?? {}), ...patch };\n const payload = JSON.stringify(merged);\n const ts = toDbTimestamp(this.db, new Date());\n\n await executeStatement(this.db, sql`\n UPDATE identity_edge_node\n SET metadata = ${payload},\n updated_at = ${ts}\n WHERE id = ${nodeId}\n `);\n }\n\n public async getNodeMetadata(nodeId: string): Promise<{ nodeId: string; metadata: Record<string, unknown> | null; lastSeen?: Date } | undefined> {\n const result = await executeQuery(this.db, sql`\n SELECT id, metadata, last_seen\n FROM identity_edge_node\n WHERE id = ${nodeId}\n LIMIT 1\n `);\n if (result.rows.length === 0) {\n return undefined;\n }\n const row = result.rows[0] as any;\n return {\n nodeId: String(row.id),\n metadata: typeof row.metadata === 'string' ? JSON.parse(row.metadata) : (row.metadata ?? null),\n lastSeen: fromDbTimestamp(row.last_seen),\n };\n }\n\n public async replaceNodePods(nodeId: string, pods: string[]): Promise<void> {\n await this.db.transaction(async (tx: IdentityDatabase) => {\n await tx.execute(sql`DELETE FROM identity_edge_node_pod WHERE node_id = ${nodeId}`);\n if (pods.length > 0) {\n const values = pods.map((baseUrl) => sql`(${nodeId}, ${baseUrl})`);\n await tx.execute(sql`\n INSERT INTO identity_edge_node_pod (node_id, base_url)\n VALUES ${sql.join(values, sql`, `)}\n ON CONFLICT DO NOTHING\n `);\n }\n });\n }\n\n public async findNodeByResourcePath(path: string): Promise<{ nodeId: string; baseUrl: string; accessMode?: string; metadata?: Record<string, unknown> | null } | undefined> {\n const result = await executeQuery(this.db, sql`\n SELECT en.id,\n en.access_mode,\n en.metadata,\n pods.base_url\n FROM identity_edge_node_pod pods\n JOIN identity_edge_node en ON en.id = pods.node_id\n WHERE ${path} LIKE pods.base_url || '%'\n ORDER BY length(pods.base_url) DESC\n LIMIT 1\n `);\n if (result.rows.length === 0) {\n return undefined;\n }\n const row = result.rows[0] as any;\n return {\n nodeId: String(row.id),\n baseUrl: String(row.base_url),\n accessMode: row.access_mode ? String(row.access_mode) : undefined,\n metadata: row.metadata ?? null,\n };\n }\n\n public async findNodeBySubdomain(hostname: string): Promise<{ nodeId: string; accessMode?: string; metadata?: Record<string, unknown> | null; subdomain?: string } | undefined> {\n const normalized = hostname.trim().toLowerCase();\n if (normalized.length === 0) {\n return undefined;\n }\n const result = await executeQuery(this.db, sql`\n SELECT id, access_mode, metadata, subdomain\n FROM identity_edge_node\n WHERE subdomain = ${normalized}\n LIMIT 1\n `);\n if (result.rows.length === 0) {\n return undefined;\n }\n const row = result.rows[0] as any;\n return {\n nodeId: String(row.id),\n accessMode: row.access_mode ? String(row.access_mode) : undefined,\n metadata: row.metadata ?? null,\n subdomain: row.subdomain ? String(row.subdomain) : undefined,\n };\n }\n\n public matchesToken(tokenHash: string, token: string): boolean {\n if (!tokenHash || typeof tokenHash !== 'string') {\n return false;\n }\n try {\n const expected = Buffer.from(tokenHash, 'hex');\n const actual = createHash('sha256').update(token).digest();\n if (expected.length !== actual.length) {\n return false;\n }\n return timingSafeEqual(expected, actual);\n } catch {\n return false;\n }\n }\n\n /**\n * Get node capabilities and related information for admin queries\n */\n public async getNodeCapabilities(nodeId: string): Promise<{\n nodeId: string;\n capabilities: Record<string, unknown> | null;\n stringCapabilities: string[] | null;\n accessMode: string | null;\n lastSeen: Date | null;\n connectivityStatus: string | null;\n } | undefined> {\n const row = await this.db\n .select({\n id: edgeNodes.id,\n capabilities: edgeNodes.capabilities,\n metadata: edgeNodes.metadata,\n accessMode: edgeNodes.accessMode,\n lastSeen: edgeNodes.lastSeen,\n connectivityStatus: edgeNodes.connectivityStatus,\n })\n .from(edgeNodes)\n .where(eq(edgeNodes.id, nodeId))\n .limit(1);\n\n if (row.length === 0) {\n return undefined;\n }\n\n const node = row[0];\n const metadata = node.metadata as Record<string, unknown> | null;\n \n return {\n nodeId: node.id,\n capabilities: node.capabilities as Record<string, unknown> | null,\n stringCapabilities: metadata?.capabilities as string[] ?? null,\n accessMode: node.accessMode,\n lastSeen: node.lastSeen,\n connectivityStatus: node.connectivityStatus,\n };\n }\n\n /**\n * List all nodes with their capability information\n */\n public async listNodeCapabilities(): Promise<Array<{\n nodeId: string;\n capabilities: Record<string, unknown> | null;\n stringCapabilities: string[] | null;\n accessMode: string | null;\n lastSeen: Date | null;\n connectivityStatus: string | null;\n }>> {\n const rows = await this.db\n .select({\n id: edgeNodes.id,\n capabilities: edgeNodes.capabilities,\n metadata: edgeNodes.metadata,\n accessMode: edgeNodes.accessMode,\n lastSeen: edgeNodes.lastSeen,\n connectivityStatus: edgeNodes.connectivityStatus,\n })\n .from(edgeNodes)\n .orderBy(edgeNodes.lastSeen);\n\n return rows.map((row: typeof rows[0]) => {\n const metadata = row.metadata as Record<string, unknown> | null;\n \n return {\n nodeId: row.id,\n capabilities: row.capabilities as Record<string, unknown> | null,\n stringCapabilities: metadata?.capabilities as string[] ?? null,\n accessMode: row.accessMode,\n lastSeen: row.lastSeen,\n connectivityStatus: row.connectivityStatus,\n };\n });\n }\n\n // ============ Center Node Methods ============\n\n /**\n * Register or update a center node in the cluster.\n * Center nodes use the same table as edge nodes but with nodeType='center'.\n */\n public async registerCenterNode(options: {\n nodeId: string;\n displayName?: string;\n internalIp: string;\n internalPort: number;\n }): Promise<{ nodeId: string; token: string }> {\n const token = randomBytes(32).toString('base64url');\n const tokenHash = createHash('sha256').update(token).digest('hex');\n const now = Math.floor(Date.now() / 1000); // Unix timestamp for SQLite compatibility\n\n // Use upsert pattern: INSERT ... ON CONFLICT UPDATE\n await executeStatement(this.db, sql`\n INSERT INTO identity_edge_node (\n id, display_name, token_hash, node_type, internal_ip, internal_port,\n connectivity_status, created_at, updated_at, last_seen\n )\n VALUES (\n ${options.nodeId}, ${options.displayName ?? null}, ${tokenHash}, 'center',\n ${options.internalIp}, ${options.internalPort}, 'unknown', ${now}, ${now}, ${now}\n )\n ON CONFLICT (id) DO UPDATE SET\n display_name = EXCLUDED.display_name,\n internal_ip = EXCLUDED.internal_ip,\n internal_port = EXCLUDED.internal_port,\n updated_at = EXCLUDED.updated_at,\n last_seen = EXCLUDED.last_seen\n `);\n\n return { nodeId: options.nodeId, token };\n }\n\n /**\n * Update center node heartbeat with internal endpoint info.\n */\n public async updateCenterNodeHeartbeat(\n nodeId: string,\n internalIp: string,\n internalPort: number,\n timestamp: Date,\n ): Promise<void> {\n const ts = Math.floor(timestamp.getTime() / 1000); // Unix timestamp for SQLite compatibility\n await executeStatement(this.db, sql`\n UPDATE identity_edge_node\n SET internal_ip = ${internalIp},\n internal_port = ${internalPort},\n last_seen = ${ts},\n updated_at = ${ts},\n connectivity_status = 'reachable'\n WHERE id = ${nodeId} AND node_type = 'center'\n `);\n }\n\n /**\n * List all center nodes in the cluster.\n */\n public async listCenterNodes(): Promise<CenterNodeInfo[]> {\n const result = await executeQuery(this.db, sql`\n SELECT id, display_name, internal_ip, internal_port, connectivity_status, last_seen\n FROM identity_edge_node\n WHERE node_type = 'center'\n ORDER BY created_at ASC\n `);\n\n return result.rows.map((row: any): CenterNodeInfo => ({\n nodeId: String(row.id),\n displayName: row.display_name == null ? undefined : String(row.display_name),\n internalIp: String(row.internal_ip ?? ''),\n internalPort: Number(row.internal_port ?? 0),\n connectivityStatus: (row.connectivity_status ?? 'unknown') as 'unknown' | 'reachable' | 'unreachable',\n lastSeen: fromDbTimestamp(row.last_seen),\n }));\n }\n\n /**\n * Get a specific center node by ID.\n */\n public async getCenterNode(nodeId: string): Promise<CenterNodeInfo | undefined> {\n const result = await executeQuery(this.db, sql`\n SELECT id, display_name, internal_ip, internal_port, connectivity_status, last_seen\n FROM identity_edge_node\n WHERE id = ${nodeId} AND node_type = 'center'\n LIMIT 1\n `);\n\n if (result.rows.length === 0) {\n return undefined;\n }\n\n const row = result.rows[0] as any;\n return {\n nodeId: String(row.id),\n displayName: row.display_name == null ? undefined : String(row.display_name),\n internalIp: String(row.internal_ip ?? ''),\n internalPort: Number(row.internal_port ?? 0),\n connectivityStatus: (row.connectivity_status ?? 'unknown') as 'unknown' | 'reachable' | 'unreachable',\n lastSeen: fromDbTimestamp(row.last_seen),\n };\n }\n\n /**\n * Find a center node by its internal endpoint (for routing).\n */\n public async findCenterNodeByEndpoint(internalIp: string, internalPort: number): Promise<CenterNodeInfo | undefined> {\n const result = await executeQuery(this.db, sql`\n SELECT id, display_name, internal_ip, internal_port, connectivity_status, last_seen\n FROM identity_edge_node\n WHERE node_type = 'center' AND internal_ip = ${internalIp} AND internal_port = ${internalPort}\n LIMIT 1\n `);\n\n if (result.rows.length === 0) {\n return undefined;\n }\n\n const row = result.rows[0] as any;\n return {\n nodeId: String(row.id),\n displayName: row.display_name == null ? undefined : String(row.display_name),\n internalIp: String(row.internal_ip ?? ''),\n internalPort: Number(row.internal_port ?? 0),\n connectivityStatus: (row.connectivity_status ?? 'unknown') as 'unknown' | 'reachable' | 'unreachable',\n lastSeen: fromDbTimestamp(row.last_seen),\n };\n }\n\n /**\n * Mark a center node as unreachable (for health checks).\n */\n public async markCenterNodeUnreachable(nodeId: string): Promise<void> {\n const ts = toDbTimestamp(this.db, new Date());\n await executeStatement(this.db, sql`\n UPDATE identity_edge_node\n SET connectivity_status = 'unreachable',\n updated_at = ${ts}\n WHERE id = ${nodeId} AND node_type = 'center'\n `);\n }\n\n /**\n * Remove a center node from the cluster.\n */\n public async removeCenterNode(nodeId: string): Promise<boolean> {\n // Note: For SQLite, we can't easily get affected row count, so just execute and return true\n await executeStatement(this.db, sql`\n DELETE FROM identity_edge_node\n WHERE id = ${nodeId} AND node_type = 'center'\n `);\n return true;\n }\n\n // ============ Account-based Node Methods ============\n\n /**\n * List nodes owned by a specific account\n */\n public async listNodesByAccount(accountId: string): Promise<Array<{\n nodeId: string;\n displayName?: string;\n capabilities: Record<string, unknown> | null;\n stringCapabilities: string[] | null;\n accessMode: string | null;\n lastSeen: Date | null;\n connectivityStatus: string | null;\n }>> {\n void accountId;\n return [];\n }\n\n /**\n * Delete a node\n */\n public async deleteNode(nodeId: string): Promise<boolean> {\n // First delete associated pods\n await executeStatement(this.db, sql`\n DELETE FROM identity_edge_node_pod WHERE node_id = ${nodeId}\n `);\n\n // Then delete the node\n const result = await executeQuery(this.db, sql`\n DELETE FROM identity_edge_node\n WHERE id = ${nodeId}\n RETURNING id\n `);\n\n return result.rows.length > 0;\n }\n\n // ============ SP (Storage Provider) Node Methods ============\n\n /**\n * Register or update an SP node (UPSERT by nodeId).\n *\n * SP 本地生成 deviceId 作为 nodeId,注册时带上来。\n * 同一 nodeId 重复注册时更新 publicUrl、token 等,保留原记录。\n * 不传 nodeId 则 Cloud 随机分配。\n */\n public async registerSpNode(options: {\n publicUrl: string;\n displayName?: string;\n /** SP 提供的设备 ID,作为 nodeId(不传则随机生成) */\n nodeId?: string;\n /** SP 提供的 serviceToken,不传则随机生成 */\n serviceToken?: string;\n }): Promise<CreateSpNodeResult> {\n const nodeId = options.nodeId || randomUUID();\n const nodeToken = randomBytes(32).toString('base64url');\n const nodeTokenHash = createHash('sha256').update(nodeToken).digest('hex');\n const serviceToken = options.serviceToken || randomBytes(32).toString('base64url');\n const now = new Date();\n const ts = toDbTimestamp(this.db, now);\n\n await executeStatement(this.db, sql`\n INSERT INTO identity_edge_node (\n id, display_name, token_hash, service_token_hash,\n node_type, public_url,\n connectivity_status, created_at, updated_at\n )\n VALUES (\n ${nodeId}, ${options.displayName ?? null}, ${nodeTokenHash}, ${serviceToken},\n 'sp', ${options.publicUrl}, 'unknown', ${ts}, ${ts}\n )\n ON CONFLICT (id) DO UPDATE SET\n display_name = EXCLUDED.display_name,\n token_hash = EXCLUDED.token_hash,\n service_token_hash = EXCLUDED.service_token_hash,\n public_url = EXCLUDED.public_url,\n updated_at = EXCLUDED.updated_at\n `);\n\n return {\n nodeId,\n nodeToken,\n serviceToken,\n createdAt: now.toISOString(),\n };\n }\n\n /**\n * Get SP node info by nodeId.\n */\n public async getSpNode(nodeId: string): Promise<SpNodeInfo | undefined> {\n const result = await executeQuery(this.db, sql`\n SELECT id, display_name, public_url, service_token_hash, last_seen\n FROM identity_edge_node\n WHERE id = ${nodeId} AND node_type = 'sp'\n LIMIT 1\n `);\n\n if (result.rows.length === 0) {\n return undefined;\n }\n\n const row = result.rows[0] as any;\n return {\n nodeId: String(row.id),\n displayName: row.display_name == null ? undefined : String(row.display_name),\n publicUrl: String(row.public_url ?? ''),\n serviceTokenHash: String(row.service_token_hash ?? ''),\n lastSeen: fromDbTimestamp(row.last_seen),\n };\n }\n}\n"]}
|
|
@@ -259,7 +259,6 @@ function ensureSqliteTables(sqlite) {
|
|
|
259
259
|
CREATE TABLE IF NOT EXISTS identity_edge_node (
|
|
260
260
|
id TEXT PRIMARY KEY,
|
|
261
261
|
display_name TEXT,
|
|
262
|
-
owner_account_id TEXT,
|
|
263
262
|
token_hash TEXT NOT NULL,
|
|
264
263
|
account_id TEXT,
|
|
265
264
|
node_type TEXT DEFAULT 'edge',
|
|
@@ -324,6 +323,14 @@ function migrateSqliteColumns(sqlite) {
|
|
|
324
323
|
// Column already exists — ignore
|
|
325
324
|
}
|
|
326
325
|
};
|
|
326
|
+
if (sqliteColumnExists(sqlite, 'identity_edge_node', 'owner_account_id')) {
|
|
327
|
+
try {
|
|
328
|
+
sqlite.exec('ALTER TABLE identity_edge_node DROP COLUMN owner_account_id');
|
|
329
|
+
}
|
|
330
|
+
catch {
|
|
331
|
+
// Older SQLite runtimes may not support DROP COLUMN. Ignore and keep runtime-compatible schema.
|
|
332
|
+
}
|
|
333
|
+
}
|
|
327
334
|
const edgeNodeColumns = [
|
|
328
335
|
['node_type', `TEXT DEFAULT 'edge'`],
|
|
329
336
|
['subdomain', 'TEXT'],
|
|
@@ -359,6 +366,10 @@ function migrateSqliteColumns(sqlite) {
|
|
|
359
366
|
addColumn('identity_pod_usage', 'token_limit_monthly', 'INTEGER');
|
|
360
367
|
addColumn('identity_pod_usage', 'period_start', 'INTEGER');
|
|
361
368
|
}
|
|
369
|
+
function sqliteColumnExists(sqlite, table, column) {
|
|
370
|
+
const rows = sqlite.prepare(`PRAGMA table_info(${table})`).all();
|
|
371
|
+
return rows.some((row) => row.name === column);
|
|
372
|
+
}
|
|
362
373
|
/**
|
|
363
374
|
* Add columns that may be missing from older PostgreSQL databases.
|
|
364
375
|
* Uses IF NOT EXISTS via information_schema check + ALTER TABLE.
|
|
@@ -434,7 +445,6 @@ async function ensurePostgresTables(pool) {
|
|
|
434
445
|
CREATE TABLE IF NOT EXISTS identity_edge_node (
|
|
435
446
|
id TEXT PRIMARY KEY,
|
|
436
447
|
display_name TEXT,
|
|
437
|
-
owner_account_id TEXT,
|
|
438
448
|
token_hash TEXT NOT NULL,
|
|
439
449
|
account_id TEXT,
|
|
440
450
|
node_type TEXT DEFAULT 'edge',
|
|
@@ -479,6 +489,7 @@ async function migratePostgresColumns(pool) {
|
|
|
479
489
|
const addColumn = async (table, column, type) => {
|
|
480
490
|
await pool.query(`ALTER TABLE ${table} ADD COLUMN IF NOT EXISTS ${column} ${type}`);
|
|
481
491
|
};
|
|
492
|
+
await pool.query('ALTER TABLE identity_edge_node DROP COLUMN IF EXISTS owner_account_id');
|
|
482
493
|
await pool.query(`
|
|
483
494
|
DO $$
|
|
484
495
|
BEGIN
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"db.js","sourceRoot":"","sources":["../../../src/identity/drizzle/db.ts"],"names":[],"mappings":"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","sourcesContent":["import { Pool, types } from 'pg';\nimport { drizzle as drizzlePg } from 'drizzle-orm/node-postgres';\nimport type { SQL } from 'drizzle-orm/sql';\nimport * as pgSchema from './schema.pg';\nimport * as sqliteSchema from './schema.sqlite';\nimport { getSharedPool, releaseSharedPool } from '../../storage/database/PostgresPoolManager';\nimport { getSqliteRuntime, type SqliteDatabase } from '../../storage/SqliteRuntime';\n\n// Use 'any' to allow both PostgreSQL and SQLite database instances\n// The actual type depends on the connection string at runtime\nexport type IdentityDatabase = any;\nexport type IdentitySchema = typeof pgSchema | typeof sqliteSchema;\n\n/**\n * Get the appropriate schema for the given database connection.\n * This provides a unified abstraction layer over PG and SQLite schemas.\n *\n * @example\n * const schema = getSchema(db);\n * await db.select().from(schema.accountUsage).where(eq(schema.accountUsage.accountId, id));\n */\nexport function getSchema(db: IdentityDatabase): typeof pgSchema | typeof sqliteSchema {\n return isDatabaseSqlite(db) ? sqliteSchema : pgSchema;\n}\n\n/**\n * Standardized query result format across databases.\n */\nexport interface QueryResult<T = Record<string, unknown>> {\n rows: T[];\n}\n\ninterface CachedConnection {\n db: IdentityDatabase;\n schema: IdentitySchema;\n isSqlite: boolean;\n close: () => Promise<void>;\n}\n\nconst dbCache = new Map<string, CachedConnection>();\nconst dbInitPromises = new WeakMap<object, Promise<void>>();\n\nconst JSON_OIDS = [114, 3802];\n\ntype SqliteDdlExecutor = Pick<SqliteDatabase, 'exec'>;\n\nfor (const oid of JSON_OIDS) {\n // Explicitly return raw string to avoid \"Type Conflict\" with CSS\n // and to satisfy PgQuintStore's parseVector expecting a string.\n types.setTypeParser(oid, (value) => value);\n}\n\n/**\n * Returns true if the connection string is a SQLite URL.\n */\nexport function isSqliteUrl(connectionString: string): boolean {\n return connectionString.startsWith('sqlite:');\n}\n\n/**\n * Get or create a Drizzle database connection with the appropriate schema.\n * Supports both PostgreSQL and SQLite.\n */\nexport function getIdentityDatabase(connectionString: string): IdentityDatabase {\n const cached = dbCache.get(connectionString);\n if (cached) {\n return cached.db;\n }\n\n if (isSqliteUrl(connectionString)) {\n const filename = connectionString.replace('sqlite:', '');\n const isMemory = filename === ':memory:' || filename.startsWith(':memory:');\n const sqliteRuntime = getSqliteRuntime();\n const sqlite = sqliteRuntime.openDatabase(isMemory ? ':memory:' : filename);\n\n if (!isMemory) {\n sqlite.pragma('journal_mode = WAL');\n sqlite.pragma('busy_timeout = 5000');\n sqlite.pragma('synchronous = NORMAL');\n }\n\n const db = sqliteRuntime.createDrizzleDatabase(sqlite);\n\n ensureSqliteTables(sqlite);\n\n dbInitPromises.set(db as object, Promise.resolve());\n dbCache.set(connectionString, {\n db,\n schema: sqliteSchema,\n isSqlite: true,\n close: async () => { sqlite.close(); },\n });\n return db;\n }\n\n // PostgreSQL: use shared pool to avoid connection exhaustion and deadlocks\n const pool = getSharedPool({ connectionString });\n const db = drizzlePg(pool);\n const initPromise = (async(): Promise<void> => {\n await ensurePostgresTables(pool);\n await migratePgColumns(pool);\n })();\n dbInitPromises.set(db as object, initPromise);\n initPromise.catch((err) => {\n console.error(`[IdentityDB] PG migration failed: ${err}`);\n });\n dbCache.set(connectionString, {\n db,\n schema: pgSchema,\n isSqlite: false,\n close: async () => { \n // Release reference to shared pool instead of ending it\n releaseSharedPool({ connectionString }); \n },\n });\n return db;\n}\n\n/**\n * Get the schema for a given connection string.\n */\nexport function getIdentitySchema(connectionString: string): IdentitySchema {\n const cached = dbCache.get(connectionString);\n if (cached) {\n return cached.schema;\n }\n // Initialize connection to populate cache\n getIdentityDatabase(connectionString);\n return dbCache.get(connectionString)!.schema;\n}\n\n/**\n * Safely get a Drizzle database connection, returning undefined on error.\n * Use this when the identity database is optional (e.g., for usage tracking).\n */\nexport function tryGetIdentityDatabase(connectionString: string): IdentityDatabase | undefined {\n try {\n return getIdentityDatabase(connectionString);\n } catch {\n return undefined;\n }\n}\n\nexport async function closeAllIdentityConnections(): Promise<void> {\n await Promise.all([...dbCache.values()].map(({ close }) => close()));\n dbCache.clear();\n}\n\n/**\n * Check if a database connection is SQLite.\n * SQLite drizzle has `all()` method but no `execute()` method.\n * PostgreSQL drizzle has `execute()` method but no `all()` method.\n */\nexport function isDatabaseSqlite(db: IdentityDatabase): boolean {\n if ((db as any)?.$xpodSqliteRuntime) {\n return true;\n }\n return typeof db.all === 'function' && typeof db.execute !== 'function';\n}\n\nasync function ensureDatabaseReady(db: IdentityDatabase): Promise<void> {\n const initPromise = dbInitPromises.get(db as object);\n if (initPromise) {\n await initPromise;\n }\n}\n\n/**\n * Execute a SQL query uniformly across PostgreSQL and SQLite.\n * Returns a standardized result with rows array.\n *\n * @example\n * const result = await executeQuery(db, sql`SELECT * FROM users WHERE id = ${userId}`);\n * if (result.rows.length > 0) { ... }\n */\nexport async function executeQuery<T = Record<string, unknown>>(\n db: IdentityDatabase,\n query: SQL,\n): Promise<QueryResult<T>> {\n await ensureDatabaseReady(db);\n if (isDatabaseSqlite(db)) {\n // SQLite: db.all() returns array directly\n const rows = db.all(query) as T[];\n return { rows };\n }\n // PostgreSQL: db.execute() returns { rows: [...] }\n return db.execute(query) as Promise<QueryResult<T>>;\n}\n\n/**\n * Execute a SQL statement that doesn't return rows (INSERT, UPDATE, DELETE).\n * Works uniformly across PostgreSQL and SQLite.\n */\nexport async function executeStatement(\n db: IdentityDatabase,\n query: SQL,\n): Promise<void> {\n await ensureDatabaseReady(db);\n if (isDatabaseSqlite(db)) {\n // SQLite: db.run() for statements\n db.run(query);\n return;\n }\n // PostgreSQL: db.execute() works for statements too\n await db.execute(query);\n}\n\n/**\n * Convert a Date to a value suitable for the database.\n * SQLite uses Unix timestamps (seconds), PostgreSQL uses Date objects.\n */\nexport function toDbTimestamp(db: IdentityDatabase, date: Date): number | Date {\n return isDatabaseSqlite(db) ? Math.floor(date.getTime() / 1000) : date;\n}\n\n/**\n * Parse a timestamp value from database result to Date.\n * Handles both Unix timestamps (SQLite) and Date objects (PostgreSQL).\n */\nexport function fromDbTimestamp(value: unknown): Date | undefined {\n if (value === null || value === undefined) {\n return undefined;\n }\n if (value instanceof Date) {\n return value;\n }\n if (typeof value === 'number') {\n return new Date(value * 1000);\n }\n if (typeof value === 'string') {\n return new Date(value);\n }\n return undefined;\n}\n\n/**\n * Ensure SQLite tables exist (simple DDL for local/dev mode).\n */\nfunction ensureSqliteTables(sqlite: SqliteDdlExecutor): void {\n sqlite.exec(`\n CREATE TABLE IF NOT EXISTS identity_account_usage (\n account_id TEXT PRIMARY KEY,\n storage_bytes INTEGER NOT NULL DEFAULT 0,\n ingress_bytes INTEGER NOT NULL DEFAULT 0,\n egress_bytes INTEGER NOT NULL DEFAULT 0,\n storage_limit_bytes INTEGER,\n bandwidth_limit_bps INTEGER,\n compute_seconds INTEGER NOT NULL DEFAULT 0,\n tokens_used INTEGER NOT NULL DEFAULT 0,\n compute_limit_seconds INTEGER,\n token_limit_monthly INTEGER,\n period_start INTEGER,\n updated_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now'))\n );\n\n CREATE TABLE IF NOT EXISTS identity_pod_usage (\n pod_id TEXT PRIMARY KEY,\n account_id TEXT NOT NULL,\n storage_bytes INTEGER NOT NULL DEFAULT 0,\n ingress_bytes INTEGER NOT NULL DEFAULT 0,\n egress_bytes INTEGER NOT NULL DEFAULT 0,\n storage_limit_bytes INTEGER,\n bandwidth_limit_bps INTEGER,\n compute_seconds INTEGER NOT NULL DEFAULT 0,\n tokens_used INTEGER NOT NULL DEFAULT 0,\n compute_limit_seconds INTEGER,\n token_limit_monthly INTEGER,\n period_start INTEGER,\n updated_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now'))\n );\n\n CREATE TABLE IF NOT EXISTS identity_edge_node (\n id TEXT PRIMARY KEY,\n display_name TEXT,\n owner_account_id TEXT,\n token_hash TEXT NOT NULL,\n account_id TEXT,\n node_type TEXT DEFAULT 'edge',\n subdomain TEXT UNIQUE,\n access_mode TEXT,\n ipv4 TEXT,\n public_port INTEGER,\n public_url TEXT,\n service_token_hash TEXT,\n provision_code_hash TEXT,\n internal_ip TEXT,\n internal_port INTEGER,\n hostname TEXT,\n ipv6 TEXT,\n version TEXT,\n capabilities TEXT,\n metadata TEXT,\n connectivity_status TEXT DEFAULT 'unknown',\n last_connectivity_check INTEGER,\n created_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now')),\n updated_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now')),\n last_seen INTEGER\n );\n\n CREATE TABLE IF NOT EXISTS identity_edge_node_pod (\n node_id TEXT NOT NULL REFERENCES identity_edge_node(id) ON DELETE CASCADE,\n base_url TEXT NOT NULL\n );\n\n CREATE TABLE IF NOT EXISTS api_client_credentials (\n client_id TEXT PRIMARY KEY,\n client_secret_encrypted TEXT NOT NULL,\n web_id TEXT NOT NULL,\n account_id TEXT NOT NULL,\n display_name TEXT,\n created_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now'))\n );\n\n CREATE TABLE IF NOT EXISTS identity_service_token (\n id TEXT PRIMARY KEY,\n token_hash TEXT NOT NULL UNIQUE,\n service_type TEXT NOT NULL,\n service_id TEXT NOT NULL,\n scopes TEXT NOT NULL,\n created_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now')),\n expires_at INTEGER\n );\n `);\n\n // Migrate existing tables: add new columns if missing\n migrateSqliteColumns(sqlite);\n}\n\n/**\n * Add columns that may be missing from older databases.\n * SQLite ALTER TABLE ADD COLUMN is idempotent-safe via try/catch.\n */\nfunction migrateSqliteColumns(sqlite: SqliteDdlExecutor): void {\n const addColumn = (table: string, column: string, type: string): void => {\n try {\n sqlite.exec(`ALTER TABLE ${table} ADD COLUMN ${column} ${type}`);\n } catch {\n // Column already exists — ignore\n }\n };\n\n const edgeNodeColumns: Array<[string, string]> = [\n [ 'node_type', `TEXT DEFAULT 'edge'` ],\n [ 'subdomain', 'TEXT' ],\n [ 'access_mode', 'TEXT' ],\n [ 'ipv4', 'TEXT' ],\n [ 'public_port', 'INTEGER' ],\n [ 'public_url', 'TEXT' ],\n [ 'service_token_hash', 'TEXT' ],\n [ 'provision_code_hash', 'TEXT' ],\n [ 'internal_ip', 'TEXT' ],\n [ 'internal_port', 'INTEGER' ],\n [ 'hostname', 'TEXT' ],\n [ 'ipv6', 'TEXT' ],\n [ 'version', 'TEXT' ],\n [ 'capabilities', 'TEXT' ],\n [ 'metadata', 'TEXT' ],\n [ 'connectivity_status', `TEXT DEFAULT 'unknown'` ],\n [ 'last_connectivity_check', 'INTEGER' ],\n [ 'last_seen', 'INTEGER' ],\n ];\n for (const [column, type] of edgeNodeColumns) {\n addColumn('identity_edge_node', column, type);\n }\n\n // Usage tables: compute/token columns\n addColumn('identity_account_usage', 'compute_seconds', 'INTEGER NOT NULL DEFAULT 0');\n addColumn('identity_account_usage', 'tokens_used', 'INTEGER NOT NULL DEFAULT 0');\n addColumn('identity_account_usage', 'compute_limit_seconds', 'INTEGER');\n addColumn('identity_account_usage', 'token_limit_monthly', 'INTEGER');\n addColumn('identity_account_usage', 'period_start', 'INTEGER');\n addColumn('identity_pod_usage', 'compute_seconds', 'INTEGER NOT NULL DEFAULT 0');\n addColumn('identity_pod_usage', 'tokens_used', 'INTEGER NOT NULL DEFAULT 0');\n addColumn('identity_pod_usage', 'compute_limit_seconds', 'INTEGER');\n addColumn('identity_pod_usage', 'token_limit_monthly', 'INTEGER');\n addColumn('identity_pod_usage', 'period_start', 'INTEGER');\n}\n\n/**\n * Add columns that may be missing from older PostgreSQL databases.\n * Uses IF NOT EXISTS via information_schema check + ALTER TABLE.\n */\nasync function migratePgColumns(pool: { query: (sql: string) => Promise<any> }): Promise<void> {\n const addColumn = async (table: string, column: string, type: string): Promise<void> => {\n try {\n await pool.query(\n `DO $$ BEGIN\n IF NOT EXISTS (\n SELECT 1 FROM information_schema.columns\n WHERE table_name = '${table}' AND column_name = '${column}'\n ) THEN\n ALTER TABLE ${table} ADD COLUMN ${column} ${type};\n END IF;\n END $$;`,\n );\n } catch {\n // Ignore errors (table might not exist yet)\n }\n };\n\n // Usage tables: compute/token columns\n await addColumn('identity_account_usage', 'compute_seconds', 'BIGINT NOT NULL DEFAULT 0');\n await addColumn('identity_account_usage', 'tokens_used', 'BIGINT NOT NULL DEFAULT 0');\n await addColumn('identity_account_usage', 'compute_limit_seconds', 'BIGINT');\n await addColumn('identity_account_usage', 'token_limit_monthly', 'BIGINT');\n await addColumn('identity_account_usage', 'period_start', 'TIMESTAMP WITH TIME ZONE');\n await addColumn('identity_pod_usage', 'compute_seconds', 'BIGINT NOT NULL DEFAULT 0');\n await addColumn('identity_pod_usage', 'tokens_used', 'BIGINT NOT NULL DEFAULT 0');\n await addColumn('identity_pod_usage', 'compute_limit_seconds', 'BIGINT');\n await addColumn('identity_pod_usage', 'token_limit_monthly', 'BIGINT');\n await addColumn('identity_pod_usage', 'period_start', 'TIMESTAMP WITH TIME ZONE');\n\n // Service token table\n try {\n await pool.query(`\n CREATE TABLE IF NOT EXISTS identity_service_token (\n id TEXT PRIMARY KEY,\n token_hash TEXT NOT NULL UNIQUE,\n service_type TEXT NOT NULL,\n service_id TEXT NOT NULL,\n scopes TEXT NOT NULL,\n created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),\n expires_at TIMESTAMP WITH TIME ZONE\n );\n `);\n } catch {\n // Ignore if already exists\n }\n}\n\n\nasync function ensurePostgresTables(pool: Pool): Promise<void> {\n await pool.query(`\n CREATE TABLE IF NOT EXISTS identity_account_usage (\n account_id TEXT PRIMARY KEY,\n storage_bytes BIGINT NOT NULL DEFAULT 0,\n ingress_bytes BIGINT NOT NULL DEFAULT 0,\n egress_bytes BIGINT NOT NULL DEFAULT 0,\n storage_limit_bytes BIGINT,\n bandwidth_limit_bps BIGINT,\n updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()\n );\n\n CREATE TABLE IF NOT EXISTS identity_pod_usage (\n pod_id TEXT PRIMARY KEY,\n account_id TEXT NOT NULL,\n storage_bytes BIGINT NOT NULL DEFAULT 0,\n ingress_bytes BIGINT NOT NULL DEFAULT 0,\n egress_bytes BIGINT NOT NULL DEFAULT 0,\n storage_limit_bytes BIGINT,\n bandwidth_limit_bps BIGINT,\n updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()\n );\n\n CREATE TABLE IF NOT EXISTS identity_edge_node (\n id TEXT PRIMARY KEY,\n display_name TEXT,\n owner_account_id TEXT,\n token_hash TEXT NOT NULL,\n account_id TEXT,\n node_type TEXT DEFAULT 'edge',\n subdomain TEXT UNIQUE,\n access_mode TEXT,\n ipv4 TEXT,\n public_port BIGINT,\n public_url TEXT,\n service_token_hash TEXT,\n provision_code_hash TEXT,\n internal_ip TEXT,\n internal_port BIGINT,\n hostname TEXT,\n ipv6 TEXT,\n version TEXT,\n capabilities JSONB,\n metadata JSONB,\n connectivity_status TEXT DEFAULT 'unknown',\n last_connectivity_check TIMESTAMPTZ,\n created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),\n updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),\n last_seen TIMESTAMPTZ\n );\n\n CREATE TABLE IF NOT EXISTS identity_edge_node_pod (\n node_id TEXT NOT NULL REFERENCES identity_edge_node(id) ON DELETE CASCADE,\n base_url TEXT NOT NULL\n );\n\n CREATE TABLE IF NOT EXISTS api_client_credentials (\n client_id TEXT PRIMARY KEY,\n client_secret_encrypted TEXT NOT NULL,\n web_id TEXT NOT NULL,\n account_id TEXT NOT NULL,\n display_name TEXT,\n created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()\n );\n `);\n\n await migratePostgresColumns(pool);\n}\n\nasync function migratePostgresColumns(pool: Pool): Promise<void> {\n const addColumn = async (table: string, column: string, type: string): Promise<void> => {\n await pool.query(`ALTER TABLE ${table} ADD COLUMN IF NOT EXISTS ${column} ${type}`);\n };\n\n await pool.query(`\n DO $$\n BEGIN\n IF EXISTS (\n SELECT 1\n FROM information_schema.columns\n WHERE table_name = 'identity_edge_node' AND column_name = 'public_ip'\n ) AND NOT EXISTS (\n SELECT 1\n FROM information_schema.columns\n WHERE table_name = 'identity_edge_node' AND column_name = 'ipv4'\n ) THEN\n ALTER TABLE identity_edge_node RENAME COLUMN public_ip TO ipv4;\n END IF;\n END $$;\n `);\n\n const edgeNodeColumns: Array<[string, string]> = [\n [ 'node_type', `TEXT DEFAULT 'edge'` ],\n [ 'subdomain', 'TEXT' ],\n [ 'access_mode', 'TEXT' ],\n [ 'ipv4', 'TEXT' ],\n [ 'public_port', 'BIGINT' ],\n [ 'public_url', 'TEXT' ],\n [ 'service_token_hash', 'TEXT' ],\n [ 'provision_code_hash', 'TEXT' ],\n [ 'internal_ip', 'TEXT' ],\n [ 'internal_port', 'BIGINT' ],\n [ 'hostname', 'TEXT' ],\n [ 'ipv6', 'TEXT' ],\n [ 'version', 'TEXT' ],\n [ 'capabilities', 'JSONB' ],\n [ 'metadata', 'JSONB' ],\n [ 'connectivity_status', `TEXT DEFAULT 'unknown'` ],\n [ 'last_connectivity_check', 'TIMESTAMPTZ' ],\n [ 'last_seen', 'TIMESTAMPTZ' ],\n ];\n for (const [column, type] of edgeNodeColumns) {\n await addColumn('identity_edge_node', column, type);\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"db.js","sourceRoot":"","sources":["../../../src/identity/drizzle/db.ts"],"names":[],"mappings":"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","sourcesContent":["import { Pool, types } from 'pg';\nimport { drizzle as drizzlePg } from 'drizzle-orm/node-postgres';\nimport type { SQL } from 'drizzle-orm/sql';\nimport * as pgSchema from './schema.pg';\nimport * as sqliteSchema from './schema.sqlite';\nimport { getSharedPool, releaseSharedPool } from '../../storage/database/PostgresPoolManager';\nimport { getSqliteRuntime, type SqliteDatabase } from '../../storage/SqliteRuntime';\n\n// Use 'any' to allow both PostgreSQL and SQLite database instances\n// The actual type depends on the connection string at runtime\nexport type IdentityDatabase = any;\nexport type IdentitySchema = typeof pgSchema | typeof sqliteSchema;\n\n/**\n * Get the appropriate schema for the given database connection.\n * This provides a unified abstraction layer over PG and SQLite schemas.\n *\n * @example\n * const schema = getSchema(db);\n * await db.select().from(schema.accountUsage).where(eq(schema.accountUsage.accountId, id));\n */\nexport function getSchema(db: IdentityDatabase): typeof pgSchema | typeof sqliteSchema {\n return isDatabaseSqlite(db) ? sqliteSchema : pgSchema;\n}\n\n/**\n * Standardized query result format across databases.\n */\nexport interface QueryResult<T = Record<string, unknown>> {\n rows: T[];\n}\n\ninterface CachedConnection {\n db: IdentityDatabase;\n schema: IdentitySchema;\n isSqlite: boolean;\n close: () => Promise<void>;\n}\n\nconst dbCache = new Map<string, CachedConnection>();\nconst dbInitPromises = new WeakMap<object, Promise<void>>();\n\nconst JSON_OIDS = [114, 3802];\n\ntype SqliteDdlExecutor = Pick<SqliteDatabase, 'exec' | 'prepare'>;\n\nfor (const oid of JSON_OIDS) {\n // Explicitly return raw string to avoid \"Type Conflict\" with CSS\n // and to satisfy PgQuintStore's parseVector expecting a string.\n types.setTypeParser(oid, (value) => value);\n}\n\n/**\n * Returns true if the connection string is a SQLite URL.\n */\nexport function isSqliteUrl(connectionString: string): boolean {\n return connectionString.startsWith('sqlite:');\n}\n\n/**\n * Get or create a Drizzle database connection with the appropriate schema.\n * Supports both PostgreSQL and SQLite.\n */\nexport function getIdentityDatabase(connectionString: string): IdentityDatabase {\n const cached = dbCache.get(connectionString);\n if (cached) {\n return cached.db;\n }\n\n if (isSqliteUrl(connectionString)) {\n const filename = connectionString.replace('sqlite:', '');\n const isMemory = filename === ':memory:' || filename.startsWith(':memory:');\n const sqliteRuntime = getSqliteRuntime();\n const sqlite = sqliteRuntime.openDatabase(isMemory ? ':memory:' : filename);\n\n if (!isMemory) {\n sqlite.pragma('journal_mode = WAL');\n sqlite.pragma('busy_timeout = 5000');\n sqlite.pragma('synchronous = NORMAL');\n }\n\n const db = sqliteRuntime.createDrizzleDatabase(sqlite);\n\n ensureSqliteTables(sqlite);\n\n dbInitPromises.set(db as object, Promise.resolve());\n dbCache.set(connectionString, {\n db,\n schema: sqliteSchema,\n isSqlite: true,\n close: async () => { sqlite.close(); },\n });\n return db;\n }\n\n // PostgreSQL: use shared pool to avoid connection exhaustion and deadlocks\n const pool = getSharedPool({ connectionString });\n const db = drizzlePg(pool);\n const initPromise = (async(): Promise<void> => {\n await ensurePostgresTables(pool);\n await migratePgColumns(pool);\n })();\n dbInitPromises.set(db as object, initPromise);\n initPromise.catch((err) => {\n console.error(`[IdentityDB] PG migration failed: ${err}`);\n });\n dbCache.set(connectionString, {\n db,\n schema: pgSchema,\n isSqlite: false,\n close: async () => { \n // Release reference to shared pool instead of ending it\n releaseSharedPool({ connectionString }); \n },\n });\n return db;\n}\n\n/**\n * Get the schema for a given connection string.\n */\nexport function getIdentitySchema(connectionString: string): IdentitySchema {\n const cached = dbCache.get(connectionString);\n if (cached) {\n return cached.schema;\n }\n // Initialize connection to populate cache\n getIdentityDatabase(connectionString);\n return dbCache.get(connectionString)!.schema;\n}\n\n/**\n * Safely get a Drizzle database connection, returning undefined on error.\n * Use this when the identity database is optional (e.g., for usage tracking).\n */\nexport function tryGetIdentityDatabase(connectionString: string): IdentityDatabase | undefined {\n try {\n return getIdentityDatabase(connectionString);\n } catch {\n return undefined;\n }\n}\n\nexport async function closeAllIdentityConnections(): Promise<void> {\n await Promise.all([...dbCache.values()].map(({ close }) => close()));\n dbCache.clear();\n}\n\n/**\n * Check if a database connection is SQLite.\n * SQLite drizzle has `all()` method but no `execute()` method.\n * PostgreSQL drizzle has `execute()` method but no `all()` method.\n */\nexport function isDatabaseSqlite(db: IdentityDatabase): boolean {\n if ((db as any)?.$xpodSqliteRuntime) {\n return true;\n }\n return typeof db.all === 'function' && typeof db.execute !== 'function';\n}\n\nasync function ensureDatabaseReady(db: IdentityDatabase): Promise<void> {\n const initPromise = dbInitPromises.get(db as object);\n if (initPromise) {\n await initPromise;\n }\n}\n\n/**\n * Execute a SQL query uniformly across PostgreSQL and SQLite.\n * Returns a standardized result with rows array.\n *\n * @example\n * const result = await executeQuery(db, sql`SELECT * FROM users WHERE id = ${userId}`);\n * if (result.rows.length > 0) { ... }\n */\nexport async function executeQuery<T = Record<string, unknown>>(\n db: IdentityDatabase,\n query: SQL,\n): Promise<QueryResult<T>> {\n await ensureDatabaseReady(db);\n if (isDatabaseSqlite(db)) {\n // SQLite: db.all() returns array directly\n const rows = db.all(query) as T[];\n return { rows };\n }\n // PostgreSQL: db.execute() returns { rows: [...] }\n return db.execute(query) as Promise<QueryResult<T>>;\n}\n\n/**\n * Execute a SQL statement that doesn't return rows (INSERT, UPDATE, DELETE).\n * Works uniformly across PostgreSQL and SQLite.\n */\nexport async function executeStatement(\n db: IdentityDatabase,\n query: SQL,\n): Promise<void> {\n await ensureDatabaseReady(db);\n if (isDatabaseSqlite(db)) {\n // SQLite: db.run() for statements\n db.run(query);\n return;\n }\n // PostgreSQL: db.execute() works for statements too\n await db.execute(query);\n}\n\n/**\n * Convert a Date to a value suitable for the database.\n * SQLite uses Unix timestamps (seconds), PostgreSQL uses Date objects.\n */\nexport function toDbTimestamp(db: IdentityDatabase, date: Date): number | Date {\n return isDatabaseSqlite(db) ? Math.floor(date.getTime() / 1000) : date;\n}\n\n/**\n * Parse a timestamp value from database result to Date.\n * Handles both Unix timestamps (SQLite) and Date objects (PostgreSQL).\n */\nexport function fromDbTimestamp(value: unknown): Date | undefined {\n if (value === null || value === undefined) {\n return undefined;\n }\n if (value instanceof Date) {\n return value;\n }\n if (typeof value === 'number') {\n return new Date(value * 1000);\n }\n if (typeof value === 'string') {\n return new Date(value);\n }\n return undefined;\n}\n\n/**\n * Ensure SQLite tables exist (simple DDL for local/dev mode).\n */\nfunction ensureSqliteTables(sqlite: SqliteDdlExecutor): void {\n sqlite.exec(`\n CREATE TABLE IF NOT EXISTS identity_account_usage (\n account_id TEXT PRIMARY KEY,\n storage_bytes INTEGER NOT NULL DEFAULT 0,\n ingress_bytes INTEGER NOT NULL DEFAULT 0,\n egress_bytes INTEGER NOT NULL DEFAULT 0,\n storage_limit_bytes INTEGER,\n bandwidth_limit_bps INTEGER,\n compute_seconds INTEGER NOT NULL DEFAULT 0,\n tokens_used INTEGER NOT NULL DEFAULT 0,\n compute_limit_seconds INTEGER,\n token_limit_monthly INTEGER,\n period_start INTEGER,\n updated_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now'))\n );\n\n CREATE TABLE IF NOT EXISTS identity_pod_usage (\n pod_id TEXT PRIMARY KEY,\n account_id TEXT NOT NULL,\n storage_bytes INTEGER NOT NULL DEFAULT 0,\n ingress_bytes INTEGER NOT NULL DEFAULT 0,\n egress_bytes INTEGER NOT NULL DEFAULT 0,\n storage_limit_bytes INTEGER,\n bandwidth_limit_bps INTEGER,\n compute_seconds INTEGER NOT NULL DEFAULT 0,\n tokens_used INTEGER NOT NULL DEFAULT 0,\n compute_limit_seconds INTEGER,\n token_limit_monthly INTEGER,\n period_start INTEGER,\n updated_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now'))\n );\n\n CREATE TABLE IF NOT EXISTS identity_edge_node (\n id TEXT PRIMARY KEY,\n display_name TEXT,\n token_hash TEXT NOT NULL,\n account_id TEXT,\n node_type TEXT DEFAULT 'edge',\n subdomain TEXT UNIQUE,\n access_mode TEXT,\n ipv4 TEXT,\n public_port INTEGER,\n public_url TEXT,\n service_token_hash TEXT,\n provision_code_hash TEXT,\n internal_ip TEXT,\n internal_port INTEGER,\n hostname TEXT,\n ipv6 TEXT,\n version TEXT,\n capabilities TEXT,\n metadata TEXT,\n connectivity_status TEXT DEFAULT 'unknown',\n last_connectivity_check INTEGER,\n created_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now')),\n updated_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now')),\n last_seen INTEGER\n );\n\n CREATE TABLE IF NOT EXISTS identity_edge_node_pod (\n node_id TEXT NOT NULL REFERENCES identity_edge_node(id) ON DELETE CASCADE,\n base_url TEXT NOT NULL\n );\n\n CREATE TABLE IF NOT EXISTS api_client_credentials (\n client_id TEXT PRIMARY KEY,\n client_secret_encrypted TEXT NOT NULL,\n web_id TEXT NOT NULL,\n account_id TEXT NOT NULL,\n display_name TEXT,\n created_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now'))\n );\n\n CREATE TABLE IF NOT EXISTS identity_service_token (\n id TEXT PRIMARY KEY,\n token_hash TEXT NOT NULL UNIQUE,\n service_type TEXT NOT NULL,\n service_id TEXT NOT NULL,\n scopes TEXT NOT NULL,\n created_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now')),\n expires_at INTEGER\n );\n `);\n\n // Migrate existing tables: add new columns if missing\n migrateSqliteColumns(sqlite);\n}\n\n/**\n * Add columns that may be missing from older databases.\n * SQLite ALTER TABLE ADD COLUMN is idempotent-safe via try/catch.\n */\nfunction migrateSqliteColumns(sqlite: SqliteDdlExecutor): void {\n const addColumn = (table: string, column: string, type: string): void => {\n try {\n sqlite.exec(`ALTER TABLE ${table} ADD COLUMN ${column} ${type}`);\n } catch {\n // Column already exists — ignore\n }\n };\n\n if (sqliteColumnExists(sqlite, 'identity_edge_node', 'owner_account_id')) {\n try {\n sqlite.exec('ALTER TABLE identity_edge_node DROP COLUMN owner_account_id');\n } catch {\n // Older SQLite runtimes may not support DROP COLUMN. Ignore and keep runtime-compatible schema.\n }\n }\n const edgeNodeColumns: Array<[string, string]> = [\n [ 'node_type', `TEXT DEFAULT 'edge'` ],\n [ 'subdomain', 'TEXT' ],\n [ 'access_mode', 'TEXT' ],\n [ 'ipv4', 'TEXT' ],\n [ 'public_port', 'INTEGER' ],\n [ 'public_url', 'TEXT' ],\n [ 'service_token_hash', 'TEXT' ],\n [ 'provision_code_hash', 'TEXT' ],\n [ 'internal_ip', 'TEXT' ],\n [ 'internal_port', 'INTEGER' ],\n [ 'hostname', 'TEXT' ],\n [ 'ipv6', 'TEXT' ],\n [ 'version', 'TEXT' ],\n [ 'capabilities', 'TEXT' ],\n [ 'metadata', 'TEXT' ],\n [ 'connectivity_status', `TEXT DEFAULT 'unknown'` ],\n [ 'last_connectivity_check', 'INTEGER' ],\n [ 'last_seen', 'INTEGER' ],\n ];\n for (const [column, type] of edgeNodeColumns) {\n addColumn('identity_edge_node', column, type);\n }\n\n // Usage tables: compute/token columns\n addColumn('identity_account_usage', 'compute_seconds', 'INTEGER NOT NULL DEFAULT 0');\n addColumn('identity_account_usage', 'tokens_used', 'INTEGER NOT NULL DEFAULT 0');\n addColumn('identity_account_usage', 'compute_limit_seconds', 'INTEGER');\n addColumn('identity_account_usage', 'token_limit_monthly', 'INTEGER');\n addColumn('identity_account_usage', 'period_start', 'INTEGER');\n addColumn('identity_pod_usage', 'compute_seconds', 'INTEGER NOT NULL DEFAULT 0');\n addColumn('identity_pod_usage', 'tokens_used', 'INTEGER NOT NULL DEFAULT 0');\n addColumn('identity_pod_usage', 'compute_limit_seconds', 'INTEGER');\n addColumn('identity_pod_usage', 'token_limit_monthly', 'INTEGER');\n addColumn('identity_pod_usage', 'period_start', 'INTEGER');\n}\n\nfunction sqliteColumnExists(sqlite: SqliteDdlExecutor, table: string, column: string): boolean {\n const rows = sqlite.prepare<{ name: string }>(`PRAGMA table_info(${table})`).all();\n return rows.some((row) => row.name === column);\n}\n\n/**\n * Add columns that may be missing from older PostgreSQL databases.\n * Uses IF NOT EXISTS via information_schema check + ALTER TABLE.\n */\nasync function migratePgColumns(pool: { query: (sql: string) => Promise<any> }): Promise<void> {\n const addColumn = async (table: string, column: string, type: string): Promise<void> => {\n try {\n await pool.query(\n `DO $$ BEGIN\n IF NOT EXISTS (\n SELECT 1 FROM information_schema.columns\n WHERE table_name = '${table}' AND column_name = '${column}'\n ) THEN\n ALTER TABLE ${table} ADD COLUMN ${column} ${type};\n END IF;\n END $$;`,\n );\n } catch {\n // Ignore errors (table might not exist yet)\n }\n };\n\n // Usage tables: compute/token columns\n await addColumn('identity_account_usage', 'compute_seconds', 'BIGINT NOT NULL DEFAULT 0');\n await addColumn('identity_account_usage', 'tokens_used', 'BIGINT NOT NULL DEFAULT 0');\n await addColumn('identity_account_usage', 'compute_limit_seconds', 'BIGINT');\n await addColumn('identity_account_usage', 'token_limit_monthly', 'BIGINT');\n await addColumn('identity_account_usage', 'period_start', 'TIMESTAMP WITH TIME ZONE');\n await addColumn('identity_pod_usage', 'compute_seconds', 'BIGINT NOT NULL DEFAULT 0');\n await addColumn('identity_pod_usage', 'tokens_used', 'BIGINT NOT NULL DEFAULT 0');\n await addColumn('identity_pod_usage', 'compute_limit_seconds', 'BIGINT');\n await addColumn('identity_pod_usage', 'token_limit_monthly', 'BIGINT');\n await addColumn('identity_pod_usage', 'period_start', 'TIMESTAMP WITH TIME ZONE');\n\n // Service token table\n try {\n await pool.query(`\n CREATE TABLE IF NOT EXISTS identity_service_token (\n id TEXT PRIMARY KEY,\n token_hash TEXT NOT NULL UNIQUE,\n service_type TEXT NOT NULL,\n service_id TEXT NOT NULL,\n scopes TEXT NOT NULL,\n created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),\n expires_at TIMESTAMP WITH TIME ZONE\n );\n `);\n } catch {\n // Ignore if already exists\n }\n}\n\n\nasync function ensurePostgresTables(pool: Pool): Promise<void> {\n await pool.query(`\n CREATE TABLE IF NOT EXISTS identity_account_usage (\n account_id TEXT PRIMARY KEY,\n storage_bytes BIGINT NOT NULL DEFAULT 0,\n ingress_bytes BIGINT NOT NULL DEFAULT 0,\n egress_bytes BIGINT NOT NULL DEFAULT 0,\n storage_limit_bytes BIGINT,\n bandwidth_limit_bps BIGINT,\n updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()\n );\n\n CREATE TABLE IF NOT EXISTS identity_pod_usage (\n pod_id TEXT PRIMARY KEY,\n account_id TEXT NOT NULL,\n storage_bytes BIGINT NOT NULL DEFAULT 0,\n ingress_bytes BIGINT NOT NULL DEFAULT 0,\n egress_bytes BIGINT NOT NULL DEFAULT 0,\n storage_limit_bytes BIGINT,\n bandwidth_limit_bps BIGINT,\n updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()\n );\n\n CREATE TABLE IF NOT EXISTS identity_edge_node (\n id TEXT PRIMARY KEY,\n display_name TEXT,\n token_hash TEXT NOT NULL,\n account_id TEXT,\n node_type TEXT DEFAULT 'edge',\n subdomain TEXT UNIQUE,\n access_mode TEXT,\n ipv4 TEXT,\n public_port BIGINT,\n public_url TEXT,\n service_token_hash TEXT,\n provision_code_hash TEXT,\n internal_ip TEXT,\n internal_port BIGINT,\n hostname TEXT,\n ipv6 TEXT,\n version TEXT,\n capabilities JSONB,\n metadata JSONB,\n connectivity_status TEXT DEFAULT 'unknown',\n last_connectivity_check TIMESTAMPTZ,\n created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),\n updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),\n last_seen TIMESTAMPTZ\n );\n\n CREATE TABLE IF NOT EXISTS identity_edge_node_pod (\n node_id TEXT NOT NULL REFERENCES identity_edge_node(id) ON DELETE CASCADE,\n base_url TEXT NOT NULL\n );\n\n CREATE TABLE IF NOT EXISTS api_client_credentials (\n client_id TEXT PRIMARY KEY,\n client_secret_encrypted TEXT NOT NULL,\n web_id TEXT NOT NULL,\n account_id TEXT NOT NULL,\n display_name TEXT,\n created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()\n );\n `);\n\n await migratePostgresColumns(pool);\n}\n\nasync function migratePostgresColumns(pool: Pool): Promise<void> {\n const addColumn = async (table: string, column: string, type: string): Promise<void> => {\n await pool.query(`ALTER TABLE ${table} ADD COLUMN IF NOT EXISTS ${column} ${type}`);\n };\n\n await pool.query('ALTER TABLE identity_edge_node DROP COLUMN IF EXISTS owner_account_id');\n await pool.query(`\n DO $$\n BEGIN\n IF EXISTS (\n SELECT 1\n FROM information_schema.columns\n WHERE table_name = 'identity_edge_node' AND column_name = 'public_ip'\n ) AND NOT EXISTS (\n SELECT 1\n FROM information_schema.columns\n WHERE table_name = 'identity_edge_node' AND column_name = 'ipv4'\n ) THEN\n ALTER TABLE identity_edge_node RENAME COLUMN public_ip TO ipv4;\n END IF;\n END $$;\n `);\n\n const edgeNodeColumns: Array<[string, string]> = [\n [ 'node_type', `TEXT DEFAULT 'edge'` ],\n [ 'subdomain', 'TEXT' ],\n [ 'access_mode', 'TEXT' ],\n [ 'ipv4', 'TEXT' ],\n [ 'public_port', 'BIGINT' ],\n [ 'public_url', 'TEXT' ],\n [ 'service_token_hash', 'TEXT' ],\n [ 'provision_code_hash', 'TEXT' ],\n [ 'internal_ip', 'TEXT' ],\n [ 'internal_port', 'BIGINT' ],\n [ 'hostname', 'TEXT' ],\n [ 'ipv6', 'TEXT' ],\n [ 'version', 'TEXT' ],\n [ 'capabilities', 'JSONB' ],\n [ 'metadata', 'JSONB' ],\n [ 'connectivity_status', `TEXT DEFAULT 'unknown'` ],\n [ 'last_connectivity_check', 'TIMESTAMPTZ' ],\n [ 'last_seen', 'TIMESTAMPTZ' ],\n ];\n for (const [column, type] of edgeNodeColumns) {\n await addColumn('identity_edge_node', column, type);\n }\n}\n"]}
|
|
@@ -79,7 +79,6 @@ exports.ddnsRecords = (0, pg_core_1.pgTable)('identity_ddns_record', {
|
|
|
79
79
|
});
|
|
80
80
|
exports.edgeNodes = (0, pg_core_1.pgTable)('identity_edge_node', {
|
|
81
81
|
id: (0, pg_core_1.text)('id').primaryKey(),
|
|
82
|
-
ownerAccountId: (0, pg_core_1.text)('owner_account_id'), // Owner of the node
|
|
83
82
|
displayName: (0, pg_core_1.text)('display_name'),
|
|
84
83
|
tokenHash: (0, pg_core_1.text)('token_hash').notNull(),
|
|
85
84
|
nodeType: (0, pg_core_1.text)('node_type').default('edge'), // 'center' | 'edge' | 'sp'
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema.pg.js","sourceRoot":"","sources":["../../../src/identity/drizzle/schema.pg.ts"],"names":[],"mappings":"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","sourcesContent":["import { pgTable, text, timestamp } from 'drizzle-orm/pg-core';\nimport { bigint as pgBigint } from 'drizzle-orm/pg-core/columns/bigint';\nimport { integer } from 'drizzle-orm/pg-core/columns/integer';\n\nexport const accountUsage = pgTable('identity_account_usage', {\n accountId: text('account_id').primaryKey(),\n storageBytes: pgBigint('storage_bytes', { mode: 'number' }).notNull().default(0),\n ingressBytes: pgBigint('ingress_bytes', { mode: 'number' }).notNull().default(0),\n egressBytes: pgBigint('egress_bytes', { mode: 'number' }).notNull().default(0),\n storageLimitBytes: pgBigint('storage_limit_bytes', { mode: 'number' }),\n bandwidthLimitBps: pgBigint('bandwidth_limit_bps', { mode: 'number' }),\n computeSeconds: pgBigint('compute_seconds', { mode: 'number' }).notNull().default(0),\n tokensUsed: pgBigint('tokens_used', { mode: 'number' }).notNull().default(0),\n computeLimitSeconds: pgBigint('compute_limit_seconds', { mode: 'number' }),\n tokenLimitMonthly: pgBigint('token_limit_monthly', { mode: 'number' }),\n periodStart: timestamp('period_start', { withTimezone: true, mode: 'date' }),\n updatedAt: timestamp('updated_at', { withTimezone: true, mode: 'date' }).notNull().defaultNow(),\n});\n\nexport const podUsage = pgTable('identity_pod_usage', {\n podId: text('pod_id').primaryKey(),\n accountId: text('account_id').notNull(),\n storageBytes: pgBigint('storage_bytes', { mode: 'number' }).notNull().default(0),\n ingressBytes: pgBigint('ingress_bytes', { mode: 'number' }).notNull().default(0),\n egressBytes: pgBigint('egress_bytes', { mode: 'number' }).notNull().default(0),\n storageLimitBytes: pgBigint('storage_limit_bytes', { mode: 'number' }),\n bandwidthLimitBps: pgBigint('bandwidth_limit_bps', { mode: 'number' }),\n computeSeconds: pgBigint('compute_seconds', { mode: 'number' }).notNull().default(0),\n tokensUsed: pgBigint('tokens_used', { mode: 'number' }).notNull().default(0),\n computeLimitSeconds: pgBigint('compute_limit_seconds', { mode: 'number' }),\n tokenLimitMonthly: pgBigint('token_limit_monthly', { mode: 'number' }),\n periodStart: timestamp('period_start', { withTimezone: true, mode: 'date' }),\n updatedAt: timestamp('updated_at', { withTimezone: true, mode: 'date' }).notNull().defaultNow(),\n});\n\n/**\n * WebID Profile 托管表\n * 用于身份与存储分离架构,Cloud 托管用户的 WebID Profile\n */\nexport const webidProfiles = pgTable('identity_webid_profile', {\n username: text('username').primaryKey(),\n webidUrl: text('webid_url').notNull(), // https://id.undefineds.co/alice/profile/card#me\n storageUrl: text('storage_url'), // https://alice.undefineds.xyz/ 或 https://pods.undefineds.co/alice/\n storageMode: text('storage_mode').default('cloud'), // 'cloud' | 'local' | 'custom'\n oidcIssuer: text('oidc_issuer'), // https://id.undefineds.co/\n profileData: text('profile_data'), // WebID Profile 的 JSON-LD 表示 (stored as JSON string)\n accountId: text('account_id'), // 关联的 CSS 账户 ID\n createdAt: timestamp('created_at', { withTimezone: true, mode: 'date' }).notNull().defaultNow(),\n updatedAt: timestamp('updated_at', { withTimezone: true, mode: 'date' }).notNull().defaultNow(),\n});\n\n/**\n * DDNS 域名池表\n * 管理可用的 DDNS 域名\n */\nexport const ddnsDomains = pgTable('identity_ddns_domain', {\n domain: text('domain').primaryKey(), // undefineds.xyz\n status: text('status').default('active'), // 'active' | 'suspended'\n provider: text('provider'), // 'cloudflare' | 'tencent'\n zoneId: text('zone_id'), // DNS Zone ID\n createdAt: timestamp('created_at', { withTimezone: true, mode: 'date' }).notNull().defaultNow(),\n});\n\n/**\n * DDNS 记录表\n * 已分配的子域名记录\n */\nexport const ddnsRecords = pgTable('identity_ddns_record', {\n subdomain: text('subdomain').primaryKey(), // alice\n domain: text('domain').notNull(), // undefineds.xyz\n ipAddress: text('ip_address'),\n ipv6Address: text('ipv6_address'),\n recordType: text('record_type').default('A'), // 'A' | 'AAAA'\n nodeId: text('node_id'), // 关联的节点 ID\n username: text('username'), // 关联的用户名\n status: text('status').default('active'), // 'active' | 'banned'\n bannedReason: text('banned_reason'),\n ttl: integer('ttl').default(60),\n createdAt: timestamp('created_at', { withTimezone: true, mode: 'date' }).notNull().defaultNow(),\n updatedAt: timestamp('updated_at', { withTimezone: true, mode: 'date' }).notNull().defaultNow(),\n});\n\nexport const edgeNodes = pgTable('identity_edge_node', {\n id: text('id').primaryKey(),\n ownerAccountId: text('owner_account_id'), // Owner of the node\n displayName: text('display_name'),\n tokenHash: text('token_hash').notNull(),\n nodeType: text('node_type').default('edge'), // 'center' | 'edge' | 'sp'\n subdomain: text('subdomain').unique(),\n accessMode: text('access_mode'),\n ipv4: text('ipv4'), // IPv4 地址\n publicPort: pgBigint('public_port', { mode: 'number' }),\n publicUrl: text('public_url'), // SP 的公网地址 (e.g. https://sp.example)\n serviceTokenHash: text('service_token_hash'), // Cloud → SP 回调认证 token (明文)\n provisionCodeHash: text('provision_code_hash'), // bind 时用户传入的配对码 (hash)\n internalIp: text('internal_ip'), // Internal network IP\n internalPort: pgBigint('internal_port', { mode: 'number' }),\n // Extracted from metadata\n hostname: text('hostname'), // 节点主机名\n ipv6: text('ipv6'), // IPv6 地址\n version: text('version'), // Agent 版本\n // JSON fields\n capabilities: text('capabilities'), // JSON string: 能力列表\n metadata: text('metadata'), // JSON string: 复杂对象 (tunnel, certificate, metrics)\n connectivityStatus: text('connectivity_status').default('unknown'),\n lastConnectivityCheck: timestamp('last_connectivity_check', { withTimezone: true, mode: 'date' }),\n createdAt: timestamp('created_at', { withTimezone: true, mode: 'date' }).notNull().defaultNow(),\n updatedAt: timestamp('updated_at', { withTimezone: true, mode: 'date' }).notNull().defaultNow(),\n lastSeen: timestamp('last_seen', { withTimezone: true, mode: 'date' }),\n});\n\nexport const edgeNodePods = pgTable('identity_edge_node_pod', {\n nodeId: text('node_id').notNull().references(() => edgeNodes.id, { onDelete: 'cascade' }),\n baseUrl: text('base_url').notNull(),\n});\n\nexport const apiClientCredentials = pgTable('identity_api_client_credentials', {\n clientId: text('client_id').primaryKey(),\n webId: text('web_id').notNull(),\n accountId: text('account_id').notNull(),\n displayName: text('display_name'),\n createdAt: timestamp('created_at', { withTimezone: true, mode: 'date' }).notNull().defaultNow(),\n});\n\n/**\n * Service Token 表\n * 用于服务间认证 (Business, Local SP, Cloud, Compute)\n */\nexport const serviceTokens = pgTable('identity_service_token', {\n id: text('id').primaryKey(),\n tokenHash: text('token_hash').notNull().unique(),\n serviceType: text('service_type').notNull(), // 'local' | 'business' | 'cloud' | 'compute'\n serviceId: text('service_id').notNull(),\n scopes: text('scopes').notNull(), // JSON array: [\"quota:write\",\"usage:read\"]\n createdAt: timestamp('created_at', { withTimezone: true, mode: 'date' }).notNull().defaultNow(),\n expiresAt: timestamp('expires_at', { withTimezone: true, mode: 'date' }),\n});\n"]}
|
|
1
|
+
{"version":3,"file":"schema.pg.js","sourceRoot":"","sources":["../../../src/identity/drizzle/schema.pg.ts"],"names":[],"mappings":"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","sourcesContent":["import { pgTable, text, timestamp } from 'drizzle-orm/pg-core';\nimport { bigint as pgBigint } from 'drizzle-orm/pg-core/columns/bigint';\nimport { integer } from 'drizzle-orm/pg-core/columns/integer';\n\nexport const accountUsage = pgTable('identity_account_usage', {\n accountId: text('account_id').primaryKey(),\n storageBytes: pgBigint('storage_bytes', { mode: 'number' }).notNull().default(0),\n ingressBytes: pgBigint('ingress_bytes', { mode: 'number' }).notNull().default(0),\n egressBytes: pgBigint('egress_bytes', { mode: 'number' }).notNull().default(0),\n storageLimitBytes: pgBigint('storage_limit_bytes', { mode: 'number' }),\n bandwidthLimitBps: pgBigint('bandwidth_limit_bps', { mode: 'number' }),\n computeSeconds: pgBigint('compute_seconds', { mode: 'number' }).notNull().default(0),\n tokensUsed: pgBigint('tokens_used', { mode: 'number' }).notNull().default(0),\n computeLimitSeconds: pgBigint('compute_limit_seconds', { mode: 'number' }),\n tokenLimitMonthly: pgBigint('token_limit_monthly', { mode: 'number' }),\n periodStart: timestamp('period_start', { withTimezone: true, mode: 'date' }),\n updatedAt: timestamp('updated_at', { withTimezone: true, mode: 'date' }).notNull().defaultNow(),\n});\n\nexport const podUsage = pgTable('identity_pod_usage', {\n podId: text('pod_id').primaryKey(),\n accountId: text('account_id').notNull(),\n storageBytes: pgBigint('storage_bytes', { mode: 'number' }).notNull().default(0),\n ingressBytes: pgBigint('ingress_bytes', { mode: 'number' }).notNull().default(0),\n egressBytes: pgBigint('egress_bytes', { mode: 'number' }).notNull().default(0),\n storageLimitBytes: pgBigint('storage_limit_bytes', { mode: 'number' }),\n bandwidthLimitBps: pgBigint('bandwidth_limit_bps', { mode: 'number' }),\n computeSeconds: pgBigint('compute_seconds', { mode: 'number' }).notNull().default(0),\n tokensUsed: pgBigint('tokens_used', { mode: 'number' }).notNull().default(0),\n computeLimitSeconds: pgBigint('compute_limit_seconds', { mode: 'number' }),\n tokenLimitMonthly: pgBigint('token_limit_monthly', { mode: 'number' }),\n periodStart: timestamp('period_start', { withTimezone: true, mode: 'date' }),\n updatedAt: timestamp('updated_at', { withTimezone: true, mode: 'date' }).notNull().defaultNow(),\n});\n\n/**\n * WebID Profile 托管表\n * 用于身份与存储分离架构,Cloud 托管用户的 WebID Profile\n */\nexport const webidProfiles = pgTable('identity_webid_profile', {\n username: text('username').primaryKey(),\n webidUrl: text('webid_url').notNull(), // https://id.undefineds.co/alice/profile/card#me\n storageUrl: text('storage_url'), // https://alice.undefineds.xyz/ 或 https://pods.undefineds.co/alice/\n storageMode: text('storage_mode').default('cloud'), // 'cloud' | 'local' | 'custom'\n oidcIssuer: text('oidc_issuer'), // https://id.undefineds.co/\n profileData: text('profile_data'), // WebID Profile 的 JSON-LD 表示 (stored as JSON string)\n accountId: text('account_id'), // 关联的 CSS 账户 ID\n createdAt: timestamp('created_at', { withTimezone: true, mode: 'date' }).notNull().defaultNow(),\n updatedAt: timestamp('updated_at', { withTimezone: true, mode: 'date' }).notNull().defaultNow(),\n});\n\n/**\n * DDNS 域名池表\n * 管理可用的 DDNS 域名\n */\nexport const ddnsDomains = pgTable('identity_ddns_domain', {\n domain: text('domain').primaryKey(), // undefineds.xyz\n status: text('status').default('active'), // 'active' | 'suspended'\n provider: text('provider'), // 'cloudflare' | 'tencent'\n zoneId: text('zone_id'), // DNS Zone ID\n createdAt: timestamp('created_at', { withTimezone: true, mode: 'date' }).notNull().defaultNow(),\n});\n\n/**\n * DDNS 记录表\n * 已分配的子域名记录\n */\nexport const ddnsRecords = pgTable('identity_ddns_record', {\n subdomain: text('subdomain').primaryKey(), // alice\n domain: text('domain').notNull(), // undefineds.xyz\n ipAddress: text('ip_address'),\n ipv6Address: text('ipv6_address'),\n recordType: text('record_type').default('A'), // 'A' | 'AAAA'\n nodeId: text('node_id'), // 关联的节点 ID\n username: text('username'), // 关联的用户名\n status: text('status').default('active'), // 'active' | 'banned'\n bannedReason: text('banned_reason'),\n ttl: integer('ttl').default(60),\n createdAt: timestamp('created_at', { withTimezone: true, mode: 'date' }).notNull().defaultNow(),\n updatedAt: timestamp('updated_at', { withTimezone: true, mode: 'date' }).notNull().defaultNow(),\n});\n\nexport const edgeNodes = pgTable('identity_edge_node', {\n id: text('id').primaryKey(),\n displayName: text('display_name'),\n tokenHash: text('token_hash').notNull(),\n nodeType: text('node_type').default('edge'), // 'center' | 'edge' | 'sp'\n subdomain: text('subdomain').unique(),\n accessMode: text('access_mode'),\n ipv4: text('ipv4'), // IPv4 地址\n publicPort: pgBigint('public_port', { mode: 'number' }),\n publicUrl: text('public_url'), // SP 的公网地址 (e.g. https://sp.example)\n serviceTokenHash: text('service_token_hash'), // Cloud → SP 回调认证 token (明文)\n provisionCodeHash: text('provision_code_hash'), // bind 时用户传入的配对码 (hash)\n internalIp: text('internal_ip'), // Internal network IP\n internalPort: pgBigint('internal_port', { mode: 'number' }),\n // Extracted from metadata\n hostname: text('hostname'), // 节点主机名\n ipv6: text('ipv6'), // IPv6 地址\n version: text('version'), // Agent 版本\n // JSON fields\n capabilities: text('capabilities'), // JSON string: 能力列表\n metadata: text('metadata'), // JSON string: 复杂对象 (tunnel, certificate, metrics)\n connectivityStatus: text('connectivity_status').default('unknown'),\n lastConnectivityCheck: timestamp('last_connectivity_check', { withTimezone: true, mode: 'date' }),\n createdAt: timestamp('created_at', { withTimezone: true, mode: 'date' }).notNull().defaultNow(),\n updatedAt: timestamp('updated_at', { withTimezone: true, mode: 'date' }).notNull().defaultNow(),\n lastSeen: timestamp('last_seen', { withTimezone: true, mode: 'date' }),\n});\n\nexport const edgeNodePods = pgTable('identity_edge_node_pod', {\n nodeId: text('node_id').notNull().references(() => edgeNodes.id, { onDelete: 'cascade' }),\n baseUrl: text('base_url').notNull(),\n});\n\nexport const apiClientCredentials = pgTable('identity_api_client_credentials', {\n clientId: text('client_id').primaryKey(),\n webId: text('web_id').notNull(),\n accountId: text('account_id').notNull(),\n displayName: text('display_name'),\n createdAt: timestamp('created_at', { withTimezone: true, mode: 'date' }).notNull().defaultNow(),\n});\n\n/**\n * Service Token 表\n * 用于服务间认证 (Business, Local SP, Cloud, Compute)\n */\nexport const serviceTokens = pgTable('identity_service_token', {\n id: text('id').primaryKey(),\n tokenHash: text('token_hash').notNull().unique(),\n serviceType: text('service_type').notNull(), // 'local' | 'business' | 'cloud' | 'compute'\n serviceId: text('service_id').notNull(),\n scopes: text('scopes').notNull(), // JSON array: [\"quota:write\",\"usage:read\"]\n createdAt: timestamp('created_at', { withTimezone: true, mode: 'date' }).notNull().defaultNow(),\n expiresAt: timestamp('expires_at', { withTimezone: true, mode: 'date' }),\n});\n"]}
|
|
@@ -979,25 +979,6 @@ export declare const edgeNodes: import("drizzle-orm/sqlite-core").SQLiteTableWit
|
|
|
979
979
|
}, {}, {
|
|
980
980
|
length: number | undefined;
|
|
981
981
|
}>;
|
|
982
|
-
ownerAccountId: import("drizzle-orm/sqlite-core").SQLiteColumn<{
|
|
983
|
-
name: "owner_account_id";
|
|
984
|
-
tableName: "identity_edge_node";
|
|
985
|
-
dataType: "string";
|
|
986
|
-
columnType: "SQLiteText";
|
|
987
|
-
data: string;
|
|
988
|
-
driverParam: string;
|
|
989
|
-
notNull: false;
|
|
990
|
-
hasDefault: false;
|
|
991
|
-
isPrimaryKey: false;
|
|
992
|
-
isAutoincrement: false;
|
|
993
|
-
hasRuntimeDefault: false;
|
|
994
|
-
enumValues: [string, ...string[]];
|
|
995
|
-
baseColumn: never;
|
|
996
|
-
identity: undefined;
|
|
997
|
-
generated: undefined;
|
|
998
|
-
}, {}, {
|
|
999
|
-
length: number | undefined;
|
|
1000
|
-
}>;
|
|
1001
982
|
displayName: import("drizzle-orm/sqlite-core").SQLiteColumn<{
|
|
1002
983
|
name: "display_name";
|
|
1003
984
|
tableName: "identity_edge_node";
|
|
@@ -77,7 +77,6 @@ exports.ddnsRecords = (0, sqlite_core_1.sqliteTable)('identity_ddns_record', {
|
|
|
77
77
|
});
|
|
78
78
|
exports.edgeNodes = (0, sqlite_core_1.sqliteTable)('identity_edge_node', {
|
|
79
79
|
id: (0, sqlite_core_1.text)('id').primaryKey(),
|
|
80
|
-
ownerAccountId: (0, sqlite_core_1.text)('owner_account_id'), // Owner of the node
|
|
81
80
|
displayName: (0, sqlite_core_1.text)('display_name'),
|
|
82
81
|
tokenHash: (0, sqlite_core_1.text)('token_hash').notNull(),
|
|
83
82
|
nodeType: (0, sqlite_core_1.text)('node_type').default('edge'), // 'center' | 'edge' | 'sp'
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema.sqlite.js","sourceRoot":"","sources":["../../../src/identity/drizzle/schema.sqlite.ts"],"names":[],"mappings":"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","sourcesContent":["import { sqliteTable, text, integer } from 'drizzle-orm/sqlite-core';\n\nexport const accountUsage = sqliteTable('identity_account_usage', {\n accountId: text('account_id').primaryKey(),\n storageBytes: integer('storage_bytes').notNull().default(0),\n ingressBytes: integer('ingress_bytes').notNull().default(0),\n egressBytes: integer('egress_bytes').notNull().default(0),\n storageLimitBytes: integer('storage_limit_bytes'),\n bandwidthLimitBps: integer('bandwidth_limit_bps'),\n computeSeconds: integer('compute_seconds').notNull().default(0),\n tokensUsed: integer('tokens_used').notNull().default(0),\n computeLimitSeconds: integer('compute_limit_seconds'),\n tokenLimitMonthly: integer('token_limit_monthly'),\n periodStart: integer('period_start'),\n updatedAt: integer('updated_at').notNull().$defaultFn(() => Math.floor(Date.now() / 1000)),\n});\n\nexport const podUsage = sqliteTable('identity_pod_usage', {\n podId: text('pod_id').primaryKey(),\n accountId: text('account_id').notNull(),\n storageBytes: integer('storage_bytes').notNull().default(0),\n ingressBytes: integer('ingress_bytes').notNull().default(0),\n egressBytes: integer('egress_bytes').notNull().default(0),\n storageLimitBytes: integer('storage_limit_bytes'),\n bandwidthLimitBps: integer('bandwidth_limit_bps'),\n computeSeconds: integer('compute_seconds').notNull().default(0),\n tokensUsed: integer('tokens_used').notNull().default(0),\n computeLimitSeconds: integer('compute_limit_seconds'),\n tokenLimitMonthly: integer('token_limit_monthly'),\n periodStart: integer('period_start'),\n updatedAt: integer('updated_at').notNull().$defaultFn(() => Math.floor(Date.now() / 1000)),\n});\n\n/**\n * WebID Profile 托管表\n * 用于身份与存储分离架构,Cloud 托管用户的 WebID Profile\n */\nexport const webidProfiles = sqliteTable('identity_webid_profile', {\n username: text('username').primaryKey(),\n webidUrl: text('webid_url').notNull(), // https://id.undefineds.co/alice/profile/card#me\n storageUrl: text('storage_url'), // https://alice.undefineds.xyz/ 或 https://pods.undefineds.co/alice/\n storageMode: text('storage_mode').default('cloud'), // 'cloud' | 'local' | 'custom'\n oidcIssuer: text('oidc_issuer'), // https://id.undefineds.co/\n profileData: text('profile_data', { mode: 'json' }), // WebID Profile 的 JSON-LD 表示\n accountId: text('account_id'), // 关联的 CSS 账户 ID\n createdAt: integer('created_at').notNull().$defaultFn(() => Math.floor(Date.now() / 1000)),\n updatedAt: integer('updated_at').notNull().$defaultFn(() => Math.floor(Date.now() / 1000)),\n});\n\n/**\n * DDNS 域名池表\n * 管理可用的 DDNS 域名\n */\nexport const ddnsDomains = sqliteTable('identity_ddns_domain', {\n domain: text('domain').primaryKey(), // undefineds.xyz\n status: text('status').default('active'), // 'active' | 'suspended'\n provider: text('provider'), // 'cloudflare' | 'tencent'\n zoneId: text('zone_id'), // DNS Zone ID\n createdAt: integer('created_at').notNull().$defaultFn(() => Math.floor(Date.now() / 1000)),\n});\n\n/**\n * DDNS 记录表\n * 已分配的子域名记录\n */\nexport const ddnsRecords = sqliteTable('identity_ddns_record', {\n subdomain: text('subdomain').primaryKey(), // alice\n domain: text('domain').notNull(), // undefineds.xyz\n ipAddress: text('ip_address'),\n ipv6Address: text('ipv6_address'),\n recordType: text('record_type').default('A'), // 'A' | 'AAAA'\n nodeId: text('node_id'), // 关联的节点 ID\n username: text('username'), // 关联的用户名\n status: text('status').default('active'), // 'active' | 'banned'\n bannedReason: text('banned_reason'),\n ttl: integer('ttl').default(60),\n createdAt: integer('created_at').notNull().$defaultFn(() => Math.floor(Date.now() / 1000)),\n updatedAt: integer('updated_at').notNull().$defaultFn(() => Math.floor(Date.now() / 1000)),\n});\n\nexport const edgeNodes = sqliteTable('identity_edge_node', {\n id: text('id').primaryKey(),\n ownerAccountId: text('owner_account_id'), // Owner of the node\n displayName: text('display_name'),\n tokenHash: text('token_hash').notNull(),\n nodeType: text('node_type').default('edge'), // 'center' | 'edge' | 'sp'\n subdomain: text('subdomain').unique(),\n accessMode: text('access_mode'),\n ipv4: text('ipv4'), // IPv4 地址\n publicPort: integer('public_port'),\n publicUrl: text('public_url'), // SP 的公网地址 (e.g. https://sp.example)\n serviceTokenHash: text('service_token_hash'), // Cloud → SP 回调认证 token (明文)\n provisionCodeHash: text('provision_code_hash'), // bind 时用户传入的配对码 (hash)\n internalIp: text('internal_ip'), // Internal network IP for center nodes\n internalPort: integer('internal_port'),\n // Extracted from metadata\n hostname: text('hostname'), // 节点主机名\n ipv6: text('ipv6'), // IPv6 地址\n version: text('version'), // Agent 版本\n // JSON fields\n capabilities: text('capabilities'), // JSON string: 能力列表\n metadata: text('metadata'), // JSON string: 复杂对象 (tunnel, certificate, metrics)\n connectivityStatus: text('connectivity_status').default('unknown'),\n lastConnectivityCheck: integer('last_connectivity_check'),\n createdAt: integer('created_at').notNull().$defaultFn(() => Math.floor(Date.now() / 1000)),\n updatedAt: integer('updated_at').notNull().$defaultFn(() => Math.floor(Date.now() / 1000)),\n lastSeen: integer('last_seen'),\n});\n\nexport const edgeNodePods = sqliteTable('identity_edge_node_pod', {\n nodeId: text('node_id').notNull().references(() => edgeNodes.id, { onDelete: 'cascade' }),\n baseUrl: text('base_url').notNull(),\n});\n\nexport const apiClientCredentials = sqliteTable('identity_api_client_credentials', {\n clientId: text('client_id').primaryKey(),\n webId: text('web_id').notNull(),\n accountId: text('account_id').notNull(),\n displayName: text('display_name'),\n createdAt: integer('created_at').notNull().$defaultFn(() => Math.floor(Date.now() / 1000)),\n});\n\n/**\n * Service Token 表\n * 用于服务间认证 (Business, Local SP, Cloud, Compute)\n */\nexport const serviceTokens = sqliteTable('identity_service_token', {\n id: text('id').primaryKey(),\n tokenHash: text('token_hash').notNull().unique(),\n serviceType: text('service_type').notNull(), // 'local' | 'business' | 'cloud' | 'compute'\n serviceId: text('service_id').notNull(),\n scopes: text('scopes').notNull(), // JSON array: [\"quota:write\",\"usage:read\"]\n createdAt: integer('created_at').notNull().$defaultFn(() => Math.floor(Date.now() / 1000)),\n expiresAt: integer('expires_at'),\n});\n"]}
|
|
1
|
+
{"version":3,"file":"schema.sqlite.js","sourceRoot":"","sources":["../../../src/identity/drizzle/schema.sqlite.ts"],"names":[],"mappings":"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","sourcesContent":["import { sqliteTable, text, integer } from 'drizzle-orm/sqlite-core';\n\nexport const accountUsage = sqliteTable('identity_account_usage', {\n accountId: text('account_id').primaryKey(),\n storageBytes: integer('storage_bytes').notNull().default(0),\n ingressBytes: integer('ingress_bytes').notNull().default(0),\n egressBytes: integer('egress_bytes').notNull().default(0),\n storageLimitBytes: integer('storage_limit_bytes'),\n bandwidthLimitBps: integer('bandwidth_limit_bps'),\n computeSeconds: integer('compute_seconds').notNull().default(0),\n tokensUsed: integer('tokens_used').notNull().default(0),\n computeLimitSeconds: integer('compute_limit_seconds'),\n tokenLimitMonthly: integer('token_limit_monthly'),\n periodStart: integer('period_start'),\n updatedAt: integer('updated_at').notNull().$defaultFn(() => Math.floor(Date.now() / 1000)),\n});\n\nexport const podUsage = sqliteTable('identity_pod_usage', {\n podId: text('pod_id').primaryKey(),\n accountId: text('account_id').notNull(),\n storageBytes: integer('storage_bytes').notNull().default(0),\n ingressBytes: integer('ingress_bytes').notNull().default(0),\n egressBytes: integer('egress_bytes').notNull().default(0),\n storageLimitBytes: integer('storage_limit_bytes'),\n bandwidthLimitBps: integer('bandwidth_limit_bps'),\n computeSeconds: integer('compute_seconds').notNull().default(0),\n tokensUsed: integer('tokens_used').notNull().default(0),\n computeLimitSeconds: integer('compute_limit_seconds'),\n tokenLimitMonthly: integer('token_limit_monthly'),\n periodStart: integer('period_start'),\n updatedAt: integer('updated_at').notNull().$defaultFn(() => Math.floor(Date.now() / 1000)),\n});\n\n/**\n * WebID Profile 托管表\n * 用于身份与存储分离架构,Cloud 托管用户的 WebID Profile\n */\nexport const webidProfiles = sqliteTable('identity_webid_profile', {\n username: text('username').primaryKey(),\n webidUrl: text('webid_url').notNull(), // https://id.undefineds.co/alice/profile/card#me\n storageUrl: text('storage_url'), // https://alice.undefineds.xyz/ 或 https://pods.undefineds.co/alice/\n storageMode: text('storage_mode').default('cloud'), // 'cloud' | 'local' | 'custom'\n oidcIssuer: text('oidc_issuer'), // https://id.undefineds.co/\n profileData: text('profile_data', { mode: 'json' }), // WebID Profile 的 JSON-LD 表示\n accountId: text('account_id'), // 关联的 CSS 账户 ID\n createdAt: integer('created_at').notNull().$defaultFn(() => Math.floor(Date.now() / 1000)),\n updatedAt: integer('updated_at').notNull().$defaultFn(() => Math.floor(Date.now() / 1000)),\n});\n\n/**\n * DDNS 域名池表\n * 管理可用的 DDNS 域名\n */\nexport const ddnsDomains = sqliteTable('identity_ddns_domain', {\n domain: text('domain').primaryKey(), // undefineds.xyz\n status: text('status').default('active'), // 'active' | 'suspended'\n provider: text('provider'), // 'cloudflare' | 'tencent'\n zoneId: text('zone_id'), // DNS Zone ID\n createdAt: integer('created_at').notNull().$defaultFn(() => Math.floor(Date.now() / 1000)),\n});\n\n/**\n * DDNS 记录表\n * 已分配的子域名记录\n */\nexport const ddnsRecords = sqliteTable('identity_ddns_record', {\n subdomain: text('subdomain').primaryKey(), // alice\n domain: text('domain').notNull(), // undefineds.xyz\n ipAddress: text('ip_address'),\n ipv6Address: text('ipv6_address'),\n recordType: text('record_type').default('A'), // 'A' | 'AAAA'\n nodeId: text('node_id'), // 关联的节点 ID\n username: text('username'), // 关联的用户名\n status: text('status').default('active'), // 'active' | 'banned'\n bannedReason: text('banned_reason'),\n ttl: integer('ttl').default(60),\n createdAt: integer('created_at').notNull().$defaultFn(() => Math.floor(Date.now() / 1000)),\n updatedAt: integer('updated_at').notNull().$defaultFn(() => Math.floor(Date.now() / 1000)),\n});\n\nexport const edgeNodes = sqliteTable('identity_edge_node', {\n id: text('id').primaryKey(),\n displayName: text('display_name'),\n tokenHash: text('token_hash').notNull(),\n nodeType: text('node_type').default('edge'), // 'center' | 'edge' | 'sp'\n subdomain: text('subdomain').unique(),\n accessMode: text('access_mode'),\n ipv4: text('ipv4'), // IPv4 地址\n publicPort: integer('public_port'),\n publicUrl: text('public_url'), // SP 的公网地址 (e.g. https://sp.example)\n serviceTokenHash: text('service_token_hash'), // Cloud → SP 回调认证 token (明文)\n provisionCodeHash: text('provision_code_hash'), // bind 时用户传入的配对码 (hash)\n internalIp: text('internal_ip'), // Internal network IP for center nodes\n internalPort: integer('internal_port'),\n // Extracted from metadata\n hostname: text('hostname'), // 节点主机名\n ipv6: text('ipv6'), // IPv6 地址\n version: text('version'), // Agent 版本\n // JSON fields\n capabilities: text('capabilities'), // JSON string: 能力列表\n metadata: text('metadata'), // JSON string: 复杂对象 (tunnel, certificate, metrics)\n connectivityStatus: text('connectivity_status').default('unknown'),\n lastConnectivityCheck: integer('last_connectivity_check'),\n createdAt: integer('created_at').notNull().$defaultFn(() => Math.floor(Date.now() / 1000)),\n updatedAt: integer('updated_at').notNull().$defaultFn(() => Math.floor(Date.now() / 1000)),\n lastSeen: integer('last_seen'),\n});\n\nexport const edgeNodePods = sqliteTable('identity_edge_node_pod', {\n nodeId: text('node_id').notNull().references(() => edgeNodes.id, { onDelete: 'cascade' }),\n baseUrl: text('base_url').notNull(),\n});\n\nexport const apiClientCredentials = sqliteTable('identity_api_client_credentials', {\n clientId: text('client_id').primaryKey(),\n webId: text('web_id').notNull(),\n accountId: text('account_id').notNull(),\n displayName: text('display_name'),\n createdAt: integer('created_at').notNull().$defaultFn(() => Math.floor(Date.now() / 1000)),\n});\n\n/**\n * Service Token 表\n * 用于服务间认证 (Business, Local SP, Cloud, Compute)\n */\nexport const serviceTokens = sqliteTable('identity_service_token', {\n id: text('id').primaryKey(),\n tokenHash: text('token_hash').notNull().unique(),\n serviceType: text('service_type').notNull(), // 'local' | 'business' | 'cloud' | 'compute'\n serviceId: text('service_id').notNull(),\n scopes: text('scopes').notNull(), // JSON array: [\"quota:write\",\"usage:read\"]\n createdAt: integer('created_at').notNull().$defaultFn(() => Math.floor(Date.now() / 1000)),\n expiresAt: integer('expires_at'),\n});\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@undefineds.co/xpod",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.6",
|
|
4
4
|
"description": "Xpod is an extended Community Solid Server, offering rich-feature, production-level Solid Pod and identity management.",
|
|
5
5
|
"repository": "https://github.com/undefinedsco/xpod",
|
|
6
6
|
"author": "developer@undefineds.co",
|