@undefineds.co/xpod 0.2.37 → 0.2.39
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/container/local.js +13 -0
- package/dist/api/container/local.js.map +1 -1
- package/dist/api/container/routes.js +14 -15
- package/dist/api/container/routes.js.map +1 -1
- package/dist/api/handlers/WebIdProfileHandler.js +61 -17
- package/dist/api/handlers/WebIdProfileHandler.js.map +1 -1
- package/dist/identity/drizzle/PodLookupRepository.d.ts +9 -0
- package/dist/identity/drizzle/PodLookupRepository.js +70 -0
- package/dist/identity/drizzle/PodLookupRepository.js.map +1 -1
- package/dist/identity/drizzle/WebIdProfileRepository.d.ts +3 -1
- package/dist/identity/drizzle/WebIdProfileRepository.js +8 -7
- package/dist/identity/drizzle/WebIdProfileRepository.js.map +1 -1
- package/dist/identity/drizzle/WebIdProfileRepository.jsonld +4 -0
- package/dist/identity/drizzle/db.js +43 -0
- package/dist/identity/drizzle/db.js.map +1 -1
- package/package.json +1 -1
|
@@ -18,12 +18,25 @@ const EdgeNodeDnsCoordinator_1 = require("../../edge/EdgeNodeDnsCoordinator");
|
|
|
18
18
|
const EdgeNodeCapabilityDetector_1 = require("../../edge/EdgeNodeCapabilityDetector");
|
|
19
19
|
const LocalNetworkManager_1 = require("../../edge/LocalNetworkManager");
|
|
20
20
|
const DdnsManager_1 = require("../../edge/DdnsManager");
|
|
21
|
+
const WebIdProfileRepository_1 = require("../../identity/drizzle/WebIdProfileRepository");
|
|
22
|
+
const PodLookupRepository_1 = require("../../identity/drizzle/PodLookupRepository");
|
|
21
23
|
/**
|
|
22
24
|
* 注册 Local 模式专属服务
|
|
23
25
|
*/
|
|
24
26
|
function registerLocalServices(container) {
|
|
25
27
|
const config = container.resolve('config');
|
|
26
28
|
const { cloudApiEndpoint, nodeId, nodeToken, cloudflareTunnelToken, sakuraTunnelToken, subdomain: subdomainConfig, } = config;
|
|
29
|
+
container.register({
|
|
30
|
+
webIdProfileRepo: (0, awilix_1.asFunction)(({ db }) => {
|
|
31
|
+
return new WebIdProfileRepository_1.WebIdProfileRepository({
|
|
32
|
+
db,
|
|
33
|
+
baseUrl: process.env.CSS_BASE_URL || `http://localhost:${process.env.CSS_PORT || 3000}`,
|
|
34
|
+
});
|
|
35
|
+
}).singleton(),
|
|
36
|
+
podLookupRepo: (0, awilix_1.asFunction)(({ db }) => {
|
|
37
|
+
return new PodLookupRepository_1.PodLookupRepository(db);
|
|
38
|
+
}).singleton(),
|
|
39
|
+
});
|
|
27
40
|
// 1. 注册 Tunnel Provider (优先 Cloudflare,其次 SakuraFRP)
|
|
28
41
|
if (cloudflareTunnelToken) {
|
|
29
42
|
container.register({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"local.js","sourceRoot":"","sources":["../../../src/api/container/local.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Local 模式服务注册\n *\n * Local 模式有两种配置:\n * - 托管式 (managed): 配置 XPOD_NODE_TOKEN,自动连接 Cloud 获取身份服务和 DDNS\n * - 独立式 (standalone): 不配置 XPOD_NODE_TOKEN,用户自己配置 CSS_BASE_URL 和 IdP\n */\n\nimport { asFunction, type AwilixContainer } from 'awilix';\nimport type { ApiContainerCradle, ApiContainerConfig } from './types';\n\nimport { SubdomainClient } from '../../subdomain/SubdomainClient';\nimport { LocalTunnelProvider } from '../../tunnel/LocalTunnelProvider';\nimport { SakuraFrpTunnelProvider } from '../../tunnel/SakuraFrpTunnelProvider';\nimport { CloudflareDnsProvider } from '../../dns/cloudflare/CloudflareDnsProvider';\nimport { SubdomainService } from '../../subdomain/SubdomainService';\nimport { EdgeNodeDnsCoordinator } from '../../edge/EdgeNodeDnsCoordinator';\nimport { EdgeNodeCapabilityDetector } from '../../edge/EdgeNodeCapabilityDetector';\nimport { LocalNetworkManager } from '../../edge/LocalNetworkManager';\nimport { DdnsManager } from '../../edge/DdnsManager';\nimport type { TunnelProvider, TunnelStatus } from '../../tunnel/TunnelProvider';\n\n/**\n * 注册 Local 模式专属服务\n */\nexport function registerLocalServices(\n container: AwilixContainer<ApiContainerCradle>,\n): void {\n const config = container.resolve('config') as ApiContainerConfig;\n\n const {\n cloudApiEndpoint,\n nodeId,\n nodeToken,\n cloudflareTunnelToken,\n sakuraTunnelToken,\n subdomain: subdomainConfig,\n } = config;\n\n // 1. 注册 Tunnel Provider (优先 Cloudflare,其次 SakuraFRP)\n if (cloudflareTunnelToken) {\n container.register({\n localTunnelProvider: asFunction(() => {\n return new LocalTunnelProvider({\n tunnelToken: cloudflareTunnelToken,\n });\n }).singleton(),\n });\n console.log('[Local] Tunnel provider registered (CLOUDFLARE_TUNNEL_TOKEN configured)');\n } else if (sakuraTunnelToken) {\n container.register({\n localTunnelProvider: asFunction(() => {\n return new SakuraFrpTunnelProvider({\n token: sakuraTunnelToken,\n });\n }).singleton(),\n });\n console.log('[Local] Tunnel provider registered (SAKURA_TUNNEL_TOKEN configured)');\n }\n\n // 2. 自适应 DNS 管理 (Self-Hosted DNS)\n // 如果配置了 Cloudflare API Token 和 Base Domain,启用本地 DNS 管理\n const apiToken = subdomainConfig?.cloudflareApiToken;\n\n // 在 Local 模式下,强制使用 CSS_BASE_URL 作为域名来源\n // 简化用户配置心智\n let baseDomain: string | undefined;\n if (process.env.CSS_BASE_URL) {\n try {\n const url = new URL(process.env.CSS_BASE_URL);\n baseDomain = url.hostname;\n } catch {\n console.warn('[Local] Invalid CSS_BASE_URL, cannot derive domain for DNS management');\n }\n }\n\n // DEBUG: 打印变量状态\n console.log(`[Local] Debug: apiToken=${apiToken ? '***' : 'undefined'}, baseDomain=${baseDomain}, CSS_BASE_URL=${process.env.CSS_BASE_URL}`);\n\n if (apiToken && baseDomain) {\n console.log('[Local] Self-hosted DNS mode detected (IPv6 Ready)');\n\n container.register({\n // DNS Provider\n dnsProvider: asFunction(() => {\n return new CloudflareDnsProvider({\n apiToken: apiToken!,\n });\n }).singleton(),\n\n // DNS Coordinator (DnsMaintainer)\n dnsCoordinator: asFunction(({ dnsProvider }: ApiContainerCradle) => {\n return new EdgeNodeDnsCoordinator({\n provider: dnsProvider!,\n rootDomain: baseDomain,\n });\n }).singleton(),\n\n // Network Detector\n capabilityDetector: asFunction(() => {\n return new EdgeNodeCapabilityDetector({\n dynamicDetection: { enableNetworkDetection: true },\n });\n }).singleton(),\n\n // Local Network Manager (Orchestrator)\n localNetworkManager: asFunction(({ capabilityDetector, dnsCoordinator }: ApiContainerCradle) => {\n return new LocalNetworkManager({\n detector: capabilityDetector!,\n dnsCoordinator: dnsCoordinator!,\n });\n }).singleton(),\n\n // Subdomain Service (Keep for API support)\n subdomainService: asFunction(({ dnsProvider, localTunnelProvider, nodeRepo }: ApiContainerCradle) => {\n // 如果没有配置 Tunnel Token,使用一个 Mock Provider\n const tunnelProvider = localTunnelProvider ?? {\n name: 'noop',\n setup: async () => { throw new Error('Tunnel not configured'); },\n start: async () => { throw new Error('Tunnel not configured'); },\n stop: async () => {},\n cleanup: async () => {},\n getStatus: () => ({ running: false, connected: false } as TunnelStatus),\n getEndpoint: () => undefined,\n } as TunnelProvider;\n\n return new SubdomainService({\n baseDomain: baseDomain!,\n dnsProvider: dnsProvider!,\n tunnelProvider,\n edgeNodeRepo: nodeRepo,\n });\n }).singleton(),\n });\n console.log(`[Local] Local DNS maintenance services registered for: ${baseDomain}`);\n // 继续进行后续逻辑,不要 return,因为用户可能既用了自管 DNS 又开启了 Managed Client\n }\n\n // 独立式:没有配置 Node Token,用户自己管理域名和 IdP\n if (!nodeToken) {\n console.log('[Local] Standalone mode (no XPOD_NODE_TOKEN)');\n console.log('[Local] User manages DNS and IdP externally');\n if (cloudflareTunnelToken) {\n console.log('[Local] Will start cloudflared with provided CLOUDFLARE_TUNNEL_TOKEN');\n }\n return;\n }\n\n // 托管式:有 Node Token,连接 Cloud\n // Cloud API endpoint 可以从 Token 解析或使用默认值\n const effectiveCloudApiEndpoint = cloudApiEndpoint || 'https://pods.undefineds.co';\n const effectiveLocalPort = parseInt(process.env.XPOD_MAIN_PORT || process.env.CSS_PORT || '3000', 10);\n\n // 从 Node Token 解析用户名作为子域名 (格式: username:secret)\n const subdomain = parseSubdomainFromToken(nodeToken);\n const tunnelProviderHint: 'cloudflare' | 'sakura_frp' | 'none' = cloudflareTunnelToken\n ? 'cloudflare'\n : sakuraTunnelToken\n ? 'sakura_frp'\n : 'none';\n\n container.register({\n subdomainClient: asFunction(() => {\n return new SubdomainClient({\n cloudApiEndpoint: effectiveCloudApiEndpoint,\n nodeId: nodeId || 'auto', // 可以从 Token 解析\n nodeToken: nodeToken!,\n });\n }).singleton(),\n\n // 注册网络检测器 (如果尚未注册)\n capabilityDetector: asFunction(() => {\n return new EdgeNodeCapabilityDetector({\n dynamicDetection: { enableNetworkDetection: true },\n });\n }).singleton(),\n\n // DDNS Manager: 自动分配和更新 DDNS\n ddnsManager: asFunction(({ subdomainClient, capabilityDetector }: ApiContainerCradle) => {\n return new DdnsManager({\n client: subdomainClient!,\n detector: capabilityDetector!,\n subdomain: subdomain || nodeId || 'auto',\n localPort: effectiveLocalPort,\n autoAllocate: true,\n tunnelProvider: tunnelProviderHint,\n });\n }).singleton(),\n });\n\n console.log('[Local] Managed mode, SubdomainClient and DdnsManager registered');\n console.log(`[Local] Cloud API endpoint: ${effectiveCloudApiEndpoint}`);\n if (subdomain) {\n console.log(`[Local] DDNS subdomain: ${subdomain}`);\n }\n if (config.oidcIssuer) {\n console.log(`[Local] Using Cloud IdP: ${config.oidcIssuer}`);\n }\n\n if (!cloudflareTunnelToken && !sakuraTunnelToken) {\n console.log('[Local] Note: No tunnel token configured, assuming direct network access');\n }\n}\n\n/**\n * 从 Node Token 解析子域名/用户名\n * Token 格式: username:secret 或 base64 编码\n */\nfunction parseSubdomainFromToken(token: string): string | undefined {\n // 尝试直接解析 username:secret 格式\n if (token.includes(':')) {\n const [username] = token.split(':');\n if (username && /^[a-z0-9][a-z0-9-]{0,61}[a-z0-9]$/.test(username)) {\n return username;\n }\n }\n\n // 尝试 base64 解码\n try {\n const decoded = Buffer.from(token, 'base64').toString('utf8');\n if (decoded.includes(':')) {\n const [username] = decoded.split(':');\n if (username && /^[a-z0-9][a-z0-9-]{0,61}[a-z0-9]$/.test(username)) {\n return username;\n }\n }\n } catch {\n // ignore\n }\n\n return undefined;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"local.js","sourceRoot":"","sources":["../../../src/api/container/local.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Local 模式服务注册\n *\n * Local 模式有两种配置:\n * - 托管式 (managed): 配置 XPOD_NODE_TOKEN,自动连接 Cloud 获取身份服务和 DDNS\n * - 独立式 (standalone): 不配置 XPOD_NODE_TOKEN,用户自己配置 CSS_BASE_URL 和 IdP\n */\n\nimport { asFunction, type AwilixContainer } from 'awilix';\nimport type { ApiContainerCradle, ApiContainerConfig } from './types';\n\nimport { SubdomainClient } from '../../subdomain/SubdomainClient';\nimport { LocalTunnelProvider } from '../../tunnel/LocalTunnelProvider';\nimport { SakuraFrpTunnelProvider } from '../../tunnel/SakuraFrpTunnelProvider';\nimport { CloudflareDnsProvider } from '../../dns/cloudflare/CloudflareDnsProvider';\nimport { SubdomainService } from '../../subdomain/SubdomainService';\nimport { EdgeNodeDnsCoordinator } from '../../edge/EdgeNodeDnsCoordinator';\nimport { EdgeNodeCapabilityDetector } from '../../edge/EdgeNodeCapabilityDetector';\nimport { LocalNetworkManager } from '../../edge/LocalNetworkManager';\nimport { DdnsManager } from '../../edge/DdnsManager';\nimport type { TunnelProvider, TunnelStatus } from '../../tunnel/TunnelProvider';\nimport { WebIdProfileRepository } from '../../identity/drizzle/WebIdProfileRepository';\nimport { PodLookupRepository } from '../../identity/drizzle/PodLookupRepository';\n\n/**\n * 注册 Local 模式专属服务\n */\nexport function registerLocalServices(\n container: AwilixContainer<ApiContainerCradle>,\n): void {\n const config = container.resolve('config') as ApiContainerConfig;\n\n const {\n cloudApiEndpoint,\n nodeId,\n nodeToken,\n cloudflareTunnelToken,\n sakuraTunnelToken,\n subdomain: subdomainConfig,\n } = config;\n\n container.register({\n webIdProfileRepo: asFunction(({ db }: ApiContainerCradle) => {\n return new WebIdProfileRepository({\n db,\n baseUrl: process.env.CSS_BASE_URL || `http://localhost:${process.env.CSS_PORT || 3000}`,\n });\n }).singleton(),\n podLookupRepo: asFunction(({ db }: ApiContainerCradle) => {\n return new PodLookupRepository(db);\n }).singleton(),\n });\n\n // 1. 注册 Tunnel Provider (优先 Cloudflare,其次 SakuraFRP)\n if (cloudflareTunnelToken) {\n container.register({\n localTunnelProvider: asFunction(() => {\n return new LocalTunnelProvider({\n tunnelToken: cloudflareTunnelToken,\n });\n }).singleton(),\n });\n console.log('[Local] Tunnel provider registered (CLOUDFLARE_TUNNEL_TOKEN configured)');\n } else if (sakuraTunnelToken) {\n container.register({\n localTunnelProvider: asFunction(() => {\n return new SakuraFrpTunnelProvider({\n token: sakuraTunnelToken,\n });\n }).singleton(),\n });\n console.log('[Local] Tunnel provider registered (SAKURA_TUNNEL_TOKEN configured)');\n }\n\n // 2. 自适应 DNS 管理 (Self-Hosted DNS)\n // 如果配置了 Cloudflare API Token 和 Base Domain,启用本地 DNS 管理\n const apiToken = subdomainConfig?.cloudflareApiToken;\n\n // 在 Local 模式下,强制使用 CSS_BASE_URL 作为域名来源\n // 简化用户配置心智\n let baseDomain: string | undefined;\n if (process.env.CSS_BASE_URL) {\n try {\n const url = new URL(process.env.CSS_BASE_URL);\n baseDomain = url.hostname;\n } catch {\n console.warn('[Local] Invalid CSS_BASE_URL, cannot derive domain for DNS management');\n }\n }\n\n // DEBUG: 打印变量状态\n console.log(`[Local] Debug: apiToken=${apiToken ? '***' : 'undefined'}, baseDomain=${baseDomain}, CSS_BASE_URL=${process.env.CSS_BASE_URL}`);\n\n if (apiToken && baseDomain) {\n console.log('[Local] Self-hosted DNS mode detected (IPv6 Ready)');\n\n container.register({\n // DNS Provider\n dnsProvider: asFunction(() => {\n return new CloudflareDnsProvider({\n apiToken: apiToken!,\n });\n }).singleton(),\n\n // DNS Coordinator (DnsMaintainer)\n dnsCoordinator: asFunction(({ dnsProvider }: ApiContainerCradle) => {\n return new EdgeNodeDnsCoordinator({\n provider: dnsProvider!,\n rootDomain: baseDomain,\n });\n }).singleton(),\n\n // Network Detector\n capabilityDetector: asFunction(() => {\n return new EdgeNodeCapabilityDetector({\n dynamicDetection: { enableNetworkDetection: true },\n });\n }).singleton(),\n\n // Local Network Manager (Orchestrator)\n localNetworkManager: asFunction(({ capabilityDetector, dnsCoordinator }: ApiContainerCradle) => {\n return new LocalNetworkManager({\n detector: capabilityDetector!,\n dnsCoordinator: dnsCoordinator!,\n });\n }).singleton(),\n\n // Subdomain Service (Keep for API support)\n subdomainService: asFunction(({ dnsProvider, localTunnelProvider, nodeRepo }: ApiContainerCradle) => {\n // 如果没有配置 Tunnel Token,使用一个 Mock Provider\n const tunnelProvider = localTunnelProvider ?? {\n name: 'noop',\n setup: async () => { throw new Error('Tunnel not configured'); },\n start: async () => { throw new Error('Tunnel not configured'); },\n stop: async () => {},\n cleanup: async () => {},\n getStatus: () => ({ running: false, connected: false } as TunnelStatus),\n getEndpoint: () => undefined,\n } as TunnelProvider;\n\n return new SubdomainService({\n baseDomain: baseDomain!,\n dnsProvider: dnsProvider!,\n tunnelProvider,\n edgeNodeRepo: nodeRepo,\n });\n }).singleton(),\n });\n console.log(`[Local] Local DNS maintenance services registered for: ${baseDomain}`);\n // 继续进行后续逻辑,不要 return,因为用户可能既用了自管 DNS 又开启了 Managed Client\n }\n\n // 独立式:没有配置 Node Token,用户自己管理域名和 IdP\n if (!nodeToken) {\n console.log('[Local] Standalone mode (no XPOD_NODE_TOKEN)');\n console.log('[Local] User manages DNS and IdP externally');\n if (cloudflareTunnelToken) {\n console.log('[Local] Will start cloudflared with provided CLOUDFLARE_TUNNEL_TOKEN');\n }\n return;\n }\n\n // 托管式:有 Node Token,连接 Cloud\n // Cloud API endpoint 可以从 Token 解析或使用默认值\n const effectiveCloudApiEndpoint = cloudApiEndpoint || 'https://pods.undefineds.co';\n const effectiveLocalPort = parseInt(process.env.XPOD_MAIN_PORT || process.env.CSS_PORT || '3000', 10);\n\n // 从 Node Token 解析用户名作为子域名 (格式: username:secret)\n const subdomain = parseSubdomainFromToken(nodeToken);\n const tunnelProviderHint: 'cloudflare' | 'sakura_frp' | 'none' = cloudflareTunnelToken\n ? 'cloudflare'\n : sakuraTunnelToken\n ? 'sakura_frp'\n : 'none';\n\n container.register({\n subdomainClient: asFunction(() => {\n return new SubdomainClient({\n cloudApiEndpoint: effectiveCloudApiEndpoint,\n nodeId: nodeId || 'auto', // 可以从 Token 解析\n nodeToken: nodeToken!,\n });\n }).singleton(),\n\n // 注册网络检测器 (如果尚未注册)\n capabilityDetector: asFunction(() => {\n return new EdgeNodeCapabilityDetector({\n dynamicDetection: { enableNetworkDetection: true },\n });\n }).singleton(),\n\n // DDNS Manager: 自动分配和更新 DDNS\n ddnsManager: asFunction(({ subdomainClient, capabilityDetector }: ApiContainerCradle) => {\n return new DdnsManager({\n client: subdomainClient!,\n detector: capabilityDetector!,\n subdomain: subdomain || nodeId || 'auto',\n localPort: effectiveLocalPort,\n autoAllocate: true,\n tunnelProvider: tunnelProviderHint,\n });\n }).singleton(),\n });\n\n console.log('[Local] Managed mode, SubdomainClient and DdnsManager registered');\n console.log(`[Local] Cloud API endpoint: ${effectiveCloudApiEndpoint}`);\n if (subdomain) {\n console.log(`[Local] DDNS subdomain: ${subdomain}`);\n }\n if (config.oidcIssuer) {\n console.log(`[Local] Using Cloud IdP: ${config.oidcIssuer}`);\n }\n\n if (!cloudflareTunnelToken && !sakuraTunnelToken) {\n console.log('[Local] Note: No tunnel token configured, assuming direct network access');\n }\n}\n\n/**\n * 从 Node Token 解析子域名/用户名\n * Token 格式: username:secret 或 base64 编码\n */\nfunction parseSubdomainFromToken(token: string): string | undefined {\n // 尝试直接解析 username:secret 格式\n if (token.includes(':')) {\n const [username] = token.split(':');\n if (username && /^[a-z0-9][a-z0-9-]{0,61}[a-z0-9]$/.test(username)) {\n return username;\n }\n }\n\n // 尝试 base64 解码\n try {\n const decoded = Buffer.from(token, 'base64').toString('utf8');\n if (decoded.includes(':')) {\n const [username] = decoded.split(':');\n if (username && /^[a-z0-9][a-z0-9-]{0,61}[a-z0-9]$/.test(username)) {\n return username;\n }\n }\n } catch {\n // ignore\n }\n\n return undefined;\n}\n"]}
|
|
@@ -108,6 +108,20 @@ function registerSharedRoutes(container, server) {
|
|
|
108
108
|
(0, ChatHandler_1.registerChatRoutes)(server, { chatService });
|
|
109
109
|
(0, ChatKitHandler_1.registerChatKitRoutes)(server, { chatKitService });
|
|
110
110
|
(0, ChatKitV1Handler_1.registerChatKitV1Routes)(server, { store: chatKitStore });
|
|
111
|
+
try {
|
|
112
|
+
const profileRepo = container.resolve('webIdProfileRepo', { allowUnregistered: true });
|
|
113
|
+
const podLookupRepo = container.resolve('podLookupRepo', { allowUnregistered: true });
|
|
114
|
+
if (profileRepo) {
|
|
115
|
+
(0, WebIdProfileHandler_1.registerWebIdProfileRoutes)(server, {
|
|
116
|
+
profileRepo: profileRepo,
|
|
117
|
+
podLookupRepo: podLookupRepo,
|
|
118
|
+
});
|
|
119
|
+
console.log('[Shared] WebID Profile routes registered');
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
catch {
|
|
123
|
+
console.log('[Shared] WebID Profile routes not registered (repo not available)');
|
|
124
|
+
}
|
|
111
125
|
// Quota & Usage API (Business 对接)
|
|
112
126
|
try {
|
|
113
127
|
const quotaService = new DrizzleQuotaService_1.DrizzleQuotaService({ identityDbUrl: config.databaseUrl });
|
|
@@ -135,21 +149,6 @@ function registerCloudRoutes(container, server) {
|
|
|
135
149
|
catch {
|
|
136
150
|
console.log('[Cloud] Subdomain routes not registered (service not available)');
|
|
137
151
|
}
|
|
138
|
-
// WebID Profile 托管服务
|
|
139
|
-
try {
|
|
140
|
-
const profileRepo = container.resolve('webIdProfileRepo', { allowUnregistered: true });
|
|
141
|
-
const podLookupRepo = container.resolve('podLookupRepo', { allowUnregistered: true });
|
|
142
|
-
if (profileRepo) {
|
|
143
|
-
(0, WebIdProfileHandler_1.registerWebIdProfileRoutes)(server, {
|
|
144
|
-
profileRepo: profileRepo,
|
|
145
|
-
podLookupRepo: podLookupRepo,
|
|
146
|
-
});
|
|
147
|
-
console.log('[Cloud] WebID Profile routes registered');
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
catch {
|
|
151
|
-
console.log('[Cloud] WebID Profile routes not registered (repo not available)');
|
|
152
|
-
}
|
|
153
152
|
// DDNS 服务
|
|
154
153
|
try {
|
|
155
154
|
const ddnsRepo = container.resolve('ddnsRepo', { allowUnregistered: true });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"routes.js","sourceRoot":"","sources":["../../../src/api/container/routes.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * 路由注册\n *\n * 根据容器中的服务注册 API 路由\n */\n\nimport type { AwilixContainer } from 'awilix';\nimport type { ApiContainerCradle, ApiContainerConfig } from './types';\nimport type { ApiServer } from '../ApiServer';\n\nimport { registerEdgeNodeSignalRoutes } from '../handlers/EdgeNodeSignalHandler';\nimport { registerNodeRoutes } from '../handlers/NodeHandler';\nimport { registerChatRoutes } from '../handlers/ChatHandler';\nimport { registerApiKeyRoutes } from '../handlers/ApiKeyHandler';\nimport { registerSubdomainRoutes } from '../handlers/SubdomainHandler';\nimport { registerSubdomainClientRoutes } from '../handlers/SubdomainClientHandler';\nimport { registerWebIdProfileRoutes } from '../handlers/WebIdProfileHandler';\nimport { registerDdnsRoutes } from '../handlers/DdnsHandler';\nimport { registerChatKitRoutes } from '../handlers/ChatKitHandler';\nimport { registerChatKitV1Routes } from '../handlers/ChatKitV1Handler';\nimport { registerDashboardRoutes } from '../handlers/DashboardHandler';\nimport { registerAdminRoutes } from '../handlers/AdminHandler';\nimport { registerAdminDdnsRoutes } from '../handlers/AdminDdnsHandler';\nimport { registerLinxCapabilitiesRoutes } from '../handlers/LinxCapabilitiesHandler';\nimport { registerProvisionRoutes, registerProvisionStatusRoute } from '../handlers/ProvisionHandler';\nimport { registerPodManagementRoutes } from '../handlers/PodManagementHandler';\nimport { registerQuotaRoutes } from '../handlers/QuotaHandler';\nimport { registerUsageRoutes } from '../handlers/UsageHandler';\nimport type { EdgeNodeRepository } from '../../identity/drizzle/EdgeNodeRepository';\nimport type { DrizzleClientCredentialsStore } from '../store/DrizzleClientCredentialsStore';\nimport { UsageRepository } from '../../storage/quota/UsageRepository';\nimport { DrizzleQuotaService } from '../../quota/DrizzleQuotaService';\nimport { LocalPodProvisioningService } from '../../provision/LocalPodProvisioningService';\nimport * as path from 'node:path';\nimport { PACKAGE_ROOT } from '../../runtime';\n\n/**\n * 注册所有 API 路由\n */\nexport function registerRoutes(container: AwilixContainer<ApiContainerCradle>): void {\n const server = container.resolve('apiServer') as ApiServer;\n const config = container.resolve('config') as ApiContainerConfig;\n\n // 公共健康检查端点\n registerHealthRoutes(server);\n\n // 共享路由\n registerSharedRoutes(container, server);\n\n // 根据 edition 注册专属路由\n if (config.edition === 'cloud') {\n registerCloudRoutes(container, server);\n } else {\n registerLocalRoutes(container, server);\n }\n}\n\n/**\n * 健康检查路由\n */\nfunction registerHealthRoutes(server: ApiServer): void {\n server.get('/health', async (_req, res) => {\n res.statusCode = 200;\n res.setHeader('Content-Type', 'application/json');\n res.end(JSON.stringify({ status: 'ok' }));\n }, { public: true });\n\n server.get('/ready', async (_req, res) => {\n res.statusCode = 200;\n res.setHeader('Content-Type', 'application/json');\n res.end(JSON.stringify({ status: 'ready' }));\n }, { public: true });\n\n // Dashboard 静态资源\n const staticDir = path.resolve(PACKAGE_ROOT, 'static/dashboard');\n registerDashboardRoutes(server, { staticDir });\n}\n\n/**\n * 共享路由 (cloud 和 local 都有)\n */\nfunction registerSharedRoutes(\n container: AwilixContainer<ApiContainerCradle>,\n server: ApiServer,\n): void {\n const nodeRepo = container.resolve('nodeRepo') as EdgeNodeRepository;\n const apiKeyStore = container.resolve('apiKeyStore') as DrizzleClientCredentialsStore;\n const chatService = container.resolve('chatService');\n const chatKitService = container.resolve('chatKitService');\n const chatKitStore = container.resolve('chatKitStore');\n const config = container.resolve('config') as ApiContainerConfig;\n\n registerEdgeNodeSignalRoutes(server, {\n repository: nodeRepo,\n dnsCoordinator: container.resolve('dnsCoordinator', { allowUnregistered: true }) as any,\n healthProbeService: container.resolve('healthProbeService', { allowUnregistered: true }) as any,\n });\n registerNodeRoutes(server, { repository: nodeRepo });\n registerApiKeyRoutes(server, { store: apiKeyStore });\n registerChatRoutes(server, { chatService });\n registerChatKitRoutes(server, { chatKitService });\n registerChatKitV1Routes(server, { store: chatKitStore });\n\n // Quota & Usage API (Business 对接)\n try {\n const quotaService = new DrizzleQuotaService({ identityDbUrl: config.databaseUrl });\n const usageRepo = new UsageRepository(container.resolve('db'));\n registerQuotaRoutes(server, { quotaService, usageRepo });\n registerUsageRoutes(server, { usageRepo });\n console.log('[Shared] Quota & Usage routes registered');\n } catch (error) {\n console.log(`[Shared] Quota & Usage routes not registered: ${error}`);\n }\n}\n\n/**\n * Cloud 模式专属路由\n */\nfunction registerCloudRoutes(\n container: AwilixContainer<ApiContainerCradle>,\n server: ApiServer,\n): void {\n // 子域名管理 API (需要 SubdomainService)\n try {\n const subdomainService = container.resolve('subdomainService') as ApiContainerCradle['subdomainService'];\n if (subdomainService) {\n registerSubdomainRoutes(server, { subdomainService });\n console.log('[Cloud] Subdomain routes registered');\n }\n } catch {\n console.log('[Cloud] Subdomain routes not registered (service not available)');\n }\n\n // WebID Profile 托管服务\n try {\n const profileRepo = container.resolve('webIdProfileRepo', { allowUnregistered: true });\n const podLookupRepo = container.resolve('podLookupRepo', { allowUnregistered: true });\n if (profileRepo) {\n registerWebIdProfileRoutes(server, {\n profileRepo: profileRepo as any,\n podLookupRepo: podLookupRepo as any,\n });\n console.log('[Cloud] WebID Profile routes registered');\n }\n } catch {\n console.log('[Cloud] WebID Profile routes not registered (repo not available)');\n }\n\n // DDNS 服务\n try {\n const ddnsRepo = container.resolve('ddnsRepo', { allowUnregistered: true });\n const dnsProvider = container.resolve('dnsProvider', { allowUnregistered: true });\n const config = container.resolve('config') as ApiContainerConfig;\n\n if (ddnsRepo) {\n const baseStorageDomain = config.subdomain?.baseStorageDomain;\n if (baseStorageDomain) {\n registerDdnsRoutes(server, {\n ddnsRepo: ddnsRepo as any,\n dnsProvider: dnsProvider as any,\n defaultDomain: baseStorageDomain,\n });\n console.log(`[Cloud] DDNS routes registered (domain: ${baseStorageDomain})`);\n } else {\n console.log('[Cloud] DDNS routes not registered (no CSS_BASE_STORAGE_DOMAIN)');\n }\n }\n } catch {\n console.log('[Cloud] DDNS routes not registered (repo not available)');\n }\n\n // SP Provision API (SP 注册)\n try {\n const nodeRepo = container.resolve('nodeRepo') as EdgeNodeRepository;\n const config = container.resolve('config') as ApiContainerConfig;\n const baseUrl = process.env.CSS_BASE_URL || 'http://localhost:3000/';\n const baseStorageDomain = config.subdomain?.baseStorageDomain;\n const ddnsRepo = container.resolve('ddnsRepo', { allowUnregistered: true }) as any;\n const dnsProvider = container.resolve('dnsProvider', { allowUnregistered: true }) as any;\n const tunnelProvider = container.resolve('tunnelProvider', { allowUnregistered: true }) as any;\n registerProvisionRoutes(server, {\n repository: nodeRepo,\n ddnsRepo,\n dnsProvider,\n tunnelProvider,\n baseUrl,\n baseStorageDomain,\n });\n console.log(`[Cloud] Provision routes registered${baseStorageDomain ? ` (baseStorageDomain: ${baseStorageDomain})` : ''}`);\n } catch {\n console.log('[Cloud] Provision routes not registered (dependencies not available)');\n }\n}\n\n/**\n * Local 模式专属路由\n */\nfunction registerLocalRoutes(\n container: AwilixContainer<ApiContainerCradle>,\n server: ApiServer,\n): void {\n registerLinxCapabilitiesRoutes(server);\n\n // Admin API (配置管理、重启)\n registerAdminRoutes(server);\n\n // DDNS status (托管式 Local 模式)\n try {\n const ddnsManager = container.resolve('ddnsManager', { allowUnregistered: true }) as any;\n registerAdminDdnsRoutes(server, { ddnsManager });\n } catch {\n // ignore\n }\n\n // 子域名客户端 API (通过 SubdomainClient 调用 Cloud)\n try {\n const subdomainClient = container.resolve('subdomainClient') as ApiContainerCradle['subdomainClient'];\n if (subdomainClient) {\n registerSubdomainClientRoutes(server, { subdomainClient });\n console.log('[Local] Subdomain client routes registered');\n }\n } catch {\n console.log('[Local] Subdomain client routes not registered (client not available)');\n }\n\n // Pod Provision API (SP 端,供 Cloud 回调创建 Pod)\n try {\n // rootDir: CSS 数据目录,默认 ./data\n const rootDir = process.env.CSS_ROOT_FILE_PATH || './data';\n // serviceToken 验证:从 SP 配置中读取\n const expectedServiceToken = process.env.XPOD_SERVICE_TOKEN;\n\n if (expectedServiceToken) {\n const config = container.resolve('config') as ApiContainerConfig;\n const baseUrl = process.env.CSS_BASE_URL || 'http://localhost:3000/';\n const sparqlEndpoint = process.env.CSS_SPARQL_ENDPOINT || process.env.SPARQL_ENDPOINT;\n const identityDbUrl = process.env.CSS_IDENTITY_DB_URL || process.env.DATABASE_URL;\n const provisioningService = sparqlEndpoint && identityDbUrl\n ? new LocalPodProvisioningService({\n baseUrl,\n rootDir,\n sparqlEndpoint,\n identityDbUrl,\n oidcIssuer: process.env.CSS_OIDC_ISSUER ?? process.env.oidcIssuer ?? config.oidcIssuer,\n })\n : undefined;\n\n registerPodManagementRoutes(server, {\n rootDir,\n verifyServiceToken: async (token: string) => token === expectedServiceToken,\n provisioningService,\n });\n console.log(`[Local] Pod provision routes registered (/provision/pods, ${provisioningService ? 'css-compatible' : 'directory-only'})`);\n } else {\n console.log('[Local] Pod provision routes not registered (XPOD_SERVICE_TOKEN not configured)');\n }\n } catch (error) {\n console.log(`[Local] Pod provision routes not registered: ${error}`);\n }\n\n // SP 状态查询 (供 Linx 查询 SP 配置状态)\n try {\n const config = container.resolve('config') as ApiContainerConfig;\n registerProvisionStatusRoute(server, {\n cloudUrl: config.cloudApiEndpoint,\n nodeId: config.nodeId,\n cloudBaseUrl: config.oidcIssuer || config.cloudApiEndpoint,\n });\n console.log('[Local] Provision status route registered (/provision/status)');\n } catch (error) {\n console.log(`[Local] Provision status route not registered: ${error}`);\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"routes.js","sourceRoot":"","sources":["../../../src/api/container/routes.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * 路由注册\n *\n * 根据容器中的服务注册 API 路由\n */\n\nimport type { AwilixContainer } from 'awilix';\nimport type { ApiContainerCradle, ApiContainerConfig } from './types';\nimport type { ApiServer } from '../ApiServer';\n\nimport { registerEdgeNodeSignalRoutes } from '../handlers/EdgeNodeSignalHandler';\nimport { registerNodeRoutes } from '../handlers/NodeHandler';\nimport { registerChatRoutes } from '../handlers/ChatHandler';\nimport { registerApiKeyRoutes } from '../handlers/ApiKeyHandler';\nimport { registerSubdomainRoutes } from '../handlers/SubdomainHandler';\nimport { registerSubdomainClientRoutes } from '../handlers/SubdomainClientHandler';\nimport { registerWebIdProfileRoutes } from '../handlers/WebIdProfileHandler';\nimport { registerDdnsRoutes } from '../handlers/DdnsHandler';\nimport { registerChatKitRoutes } from '../handlers/ChatKitHandler';\nimport { registerChatKitV1Routes } from '../handlers/ChatKitV1Handler';\nimport { registerDashboardRoutes } from '../handlers/DashboardHandler';\nimport { registerAdminRoutes } from '../handlers/AdminHandler';\nimport { registerAdminDdnsRoutes } from '../handlers/AdminDdnsHandler';\nimport { registerLinxCapabilitiesRoutes } from '../handlers/LinxCapabilitiesHandler';\nimport { registerProvisionRoutes, registerProvisionStatusRoute } from '../handlers/ProvisionHandler';\nimport { registerPodManagementRoutes } from '../handlers/PodManagementHandler';\nimport { registerQuotaRoutes } from '../handlers/QuotaHandler';\nimport { registerUsageRoutes } from '../handlers/UsageHandler';\nimport type { EdgeNodeRepository } from '../../identity/drizzle/EdgeNodeRepository';\nimport type { DrizzleClientCredentialsStore } from '../store/DrizzleClientCredentialsStore';\nimport { UsageRepository } from '../../storage/quota/UsageRepository';\nimport { DrizzleQuotaService } from '../../quota/DrizzleQuotaService';\nimport { LocalPodProvisioningService } from '../../provision/LocalPodProvisioningService';\nimport * as path from 'node:path';\nimport { PACKAGE_ROOT } from '../../runtime';\n\n/**\n * 注册所有 API 路由\n */\nexport function registerRoutes(container: AwilixContainer<ApiContainerCradle>): void {\n const server = container.resolve('apiServer') as ApiServer;\n const config = container.resolve('config') as ApiContainerConfig;\n\n // 公共健康检查端点\n registerHealthRoutes(server);\n\n // 共享路由\n registerSharedRoutes(container, server);\n\n // 根据 edition 注册专属路由\n if (config.edition === 'cloud') {\n registerCloudRoutes(container, server);\n } else {\n registerLocalRoutes(container, server);\n }\n}\n\n/**\n * 健康检查路由\n */\nfunction registerHealthRoutes(server: ApiServer): void {\n server.get('/health', async (_req, res) => {\n res.statusCode = 200;\n res.setHeader('Content-Type', 'application/json');\n res.end(JSON.stringify({ status: 'ok' }));\n }, { public: true });\n\n server.get('/ready', async (_req, res) => {\n res.statusCode = 200;\n res.setHeader('Content-Type', 'application/json');\n res.end(JSON.stringify({ status: 'ready' }));\n }, { public: true });\n\n // Dashboard 静态资源\n const staticDir = path.resolve(PACKAGE_ROOT, 'static/dashboard');\n registerDashboardRoutes(server, { staticDir });\n}\n\n/**\n * 共享路由 (cloud 和 local 都有)\n */\nfunction registerSharedRoutes(\n container: AwilixContainer<ApiContainerCradle>,\n server: ApiServer,\n): void {\n const nodeRepo = container.resolve('nodeRepo') as EdgeNodeRepository;\n const apiKeyStore = container.resolve('apiKeyStore') as DrizzleClientCredentialsStore;\n const chatService = container.resolve('chatService');\n const chatKitService = container.resolve('chatKitService');\n const chatKitStore = container.resolve('chatKitStore');\n const config = container.resolve('config') as ApiContainerConfig;\n\n registerEdgeNodeSignalRoutes(server, {\n repository: nodeRepo,\n dnsCoordinator: container.resolve('dnsCoordinator', { allowUnregistered: true }) as any,\n healthProbeService: container.resolve('healthProbeService', { allowUnregistered: true }) as any,\n });\n registerNodeRoutes(server, { repository: nodeRepo });\n registerApiKeyRoutes(server, { store: apiKeyStore });\n registerChatRoutes(server, { chatService });\n registerChatKitRoutes(server, { chatKitService });\n registerChatKitV1Routes(server, { store: chatKitStore });\n\n try {\n const profileRepo = container.resolve('webIdProfileRepo', { allowUnregistered: true });\n const podLookupRepo = container.resolve('podLookupRepo', { allowUnregistered: true });\n if (profileRepo) {\n registerWebIdProfileRoutes(server, {\n profileRepo: profileRepo as any,\n podLookupRepo: podLookupRepo as any,\n });\n console.log('[Shared] WebID Profile routes registered');\n }\n } catch {\n console.log('[Shared] WebID Profile routes not registered (repo not available)');\n }\n\n // Quota & Usage API (Business 对接)\n try {\n const quotaService = new DrizzleQuotaService({ identityDbUrl: config.databaseUrl });\n const usageRepo = new UsageRepository(container.resolve('db'));\n registerQuotaRoutes(server, { quotaService, usageRepo });\n registerUsageRoutes(server, { usageRepo });\n console.log('[Shared] Quota & Usage routes registered');\n } catch (error) {\n console.log(`[Shared] Quota & Usage routes not registered: ${error}`);\n }\n}\n\n/**\n * Cloud 模式专属路由\n */\nfunction registerCloudRoutes(\n container: AwilixContainer<ApiContainerCradle>,\n server: ApiServer,\n): void {\n // 子域名管理 API (需要 SubdomainService)\n try {\n const subdomainService = container.resolve('subdomainService') as ApiContainerCradle['subdomainService'];\n if (subdomainService) {\n registerSubdomainRoutes(server, { subdomainService });\n console.log('[Cloud] Subdomain routes registered');\n }\n } catch {\n console.log('[Cloud] Subdomain routes not registered (service not available)');\n }\n\n // DDNS 服务\n try {\n const ddnsRepo = container.resolve('ddnsRepo', { allowUnregistered: true });\n const dnsProvider = container.resolve('dnsProvider', { allowUnregistered: true });\n const config = container.resolve('config') as ApiContainerConfig;\n\n if (ddnsRepo) {\n const baseStorageDomain = config.subdomain?.baseStorageDomain;\n if (baseStorageDomain) {\n registerDdnsRoutes(server, {\n ddnsRepo: ddnsRepo as any,\n dnsProvider: dnsProvider as any,\n defaultDomain: baseStorageDomain,\n });\n console.log(`[Cloud] DDNS routes registered (domain: ${baseStorageDomain})`);\n } else {\n console.log('[Cloud] DDNS routes not registered (no CSS_BASE_STORAGE_DOMAIN)');\n }\n }\n } catch {\n console.log('[Cloud] DDNS routes not registered (repo not available)');\n }\n\n // SP Provision API (SP 注册)\n try {\n const nodeRepo = container.resolve('nodeRepo') as EdgeNodeRepository;\n const config = container.resolve('config') as ApiContainerConfig;\n const baseUrl = process.env.CSS_BASE_URL || 'http://localhost:3000/';\n const baseStorageDomain = config.subdomain?.baseStorageDomain;\n const ddnsRepo = container.resolve('ddnsRepo', { allowUnregistered: true }) as any;\n const dnsProvider = container.resolve('dnsProvider', { allowUnregistered: true }) as any;\n const tunnelProvider = container.resolve('tunnelProvider', { allowUnregistered: true }) as any;\n registerProvisionRoutes(server, {\n repository: nodeRepo,\n ddnsRepo,\n dnsProvider,\n tunnelProvider,\n baseUrl,\n baseStorageDomain,\n });\n console.log(`[Cloud] Provision routes registered${baseStorageDomain ? ` (baseStorageDomain: ${baseStorageDomain})` : ''}`);\n } catch {\n console.log('[Cloud] Provision routes not registered (dependencies not available)');\n }\n}\n\n/**\n * Local 模式专属路由\n */\nfunction registerLocalRoutes(\n container: AwilixContainer<ApiContainerCradle>,\n server: ApiServer,\n): void {\n registerLinxCapabilitiesRoutes(server);\n\n // Admin API (配置管理、重启)\n registerAdminRoutes(server);\n\n // DDNS status (托管式 Local 模式)\n try {\n const ddnsManager = container.resolve('ddnsManager', { allowUnregistered: true }) as any;\n registerAdminDdnsRoutes(server, { ddnsManager });\n } catch {\n // ignore\n }\n\n // 子域名客户端 API (通过 SubdomainClient 调用 Cloud)\n try {\n const subdomainClient = container.resolve('subdomainClient') as ApiContainerCradle['subdomainClient'];\n if (subdomainClient) {\n registerSubdomainClientRoutes(server, { subdomainClient });\n console.log('[Local] Subdomain client routes registered');\n }\n } catch {\n console.log('[Local] Subdomain client routes not registered (client not available)');\n }\n\n // Pod Provision API (SP 端,供 Cloud 回调创建 Pod)\n try {\n // rootDir: CSS 数据目录,默认 ./data\n const rootDir = process.env.CSS_ROOT_FILE_PATH || './data';\n // serviceToken 验证:从 SP 配置中读取\n const expectedServiceToken = process.env.XPOD_SERVICE_TOKEN;\n\n if (expectedServiceToken) {\n const config = container.resolve('config') as ApiContainerConfig;\n const baseUrl = process.env.CSS_BASE_URL || 'http://localhost:3000/';\n const sparqlEndpoint = process.env.CSS_SPARQL_ENDPOINT || process.env.SPARQL_ENDPOINT;\n const identityDbUrl = process.env.CSS_IDENTITY_DB_URL || process.env.DATABASE_URL;\n const provisioningService = sparqlEndpoint && identityDbUrl\n ? new LocalPodProvisioningService({\n baseUrl,\n rootDir,\n sparqlEndpoint,\n identityDbUrl,\n oidcIssuer: process.env.CSS_OIDC_ISSUER ?? process.env.oidcIssuer ?? config.oidcIssuer,\n })\n : undefined;\n\n registerPodManagementRoutes(server, {\n rootDir,\n verifyServiceToken: async (token: string) => token === expectedServiceToken,\n provisioningService,\n });\n console.log(`[Local] Pod provision routes registered (/provision/pods, ${provisioningService ? 'css-compatible' : 'directory-only'})`);\n } else {\n console.log('[Local] Pod provision routes not registered (XPOD_SERVICE_TOKEN not configured)');\n }\n } catch (error) {\n console.log(`[Local] Pod provision routes not registered: ${error}`);\n }\n\n // SP 状态查询 (供 Linx 查询 SP 配置状态)\n try {\n const config = container.resolve('config') as ApiContainerConfig;\n registerProvisionStatusRoute(server, {\n cloudUrl: config.cloudApiEndpoint,\n nodeId: config.nodeId,\n cloudBaseUrl: config.oidcIssuer || config.cloudApiEndpoint,\n });\n console.log('[Local] Provision status route registered (/provision/status)');\n } catch (error) {\n console.log(`[Local] Provision status route not registered: ${error}`);\n }\n}\n"]}
|
|
@@ -22,7 +22,7 @@ function registerWebIdProfileRoutes(server, options) {
|
|
|
22
22
|
server.get('/:username/profile/card', async (_request, response, params) => {
|
|
23
23
|
const username = decodeURIComponent(params.username);
|
|
24
24
|
try {
|
|
25
|
-
const profile = await
|
|
25
|
+
const profile = await resolveIdentityLookup(username, options);
|
|
26
26
|
if (!profile) {
|
|
27
27
|
sendError(response, 404, 'Profile not found');
|
|
28
28
|
return;
|
|
@@ -193,26 +193,16 @@ async function resolveProfileFromPods(username, options) {
|
|
|
193
193
|
if (!podLookupRepo) {
|
|
194
194
|
return null;
|
|
195
195
|
}
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
196
|
+
const webidUrl = buildHostedWebIdUrl(username);
|
|
197
|
+
const webIdMatch = await tryFindPodByWebId(podLookupRepo, webidUrl, username);
|
|
198
|
+
if (webIdMatch) {
|
|
199
|
+
return profileFromPod(username, webidUrl, webIdMatch);
|
|
199
200
|
}
|
|
200
|
-
|
|
201
|
-
logger.warn(`Pod index lookup unavailable for ${username}: ${error}`);
|
|
202
|
-
return null;
|
|
203
|
-
}
|
|
204
|
-
const match = pods.find((pod) => derivePodSlug(pod.baseUrl) === username);
|
|
201
|
+
const match = await tryFindPodByStorageSlug(podLookupRepo, username);
|
|
205
202
|
if (!match) {
|
|
206
203
|
return null;
|
|
207
204
|
}
|
|
208
|
-
|
|
209
|
-
return {
|
|
210
|
-
username,
|
|
211
|
-
webidUrl: `${storageUrl}profile/card#me`,
|
|
212
|
-
storageUrl,
|
|
213
|
-
storageMode: 'cloud',
|
|
214
|
-
oidcIssuer: deriveOrigin(storageUrl),
|
|
215
|
-
};
|
|
205
|
+
return profileFromPod(username, buildStorageWebIdUrl(match.baseUrl), match);
|
|
216
206
|
}
|
|
217
207
|
async function resolveProfileWithStorageBackfill(username, options) {
|
|
218
208
|
const { profileRepo, podLookupRepo } = options;
|
|
@@ -277,6 +267,60 @@ function derivePodSlug(baseUrl) {
|
|
|
277
267
|
return null;
|
|
278
268
|
}
|
|
279
269
|
}
|
|
270
|
+
async function tryFindPodByWebId(podLookupRepo, webidUrl, username) {
|
|
271
|
+
try {
|
|
272
|
+
if (typeof podLookupRepo.findByWebId === 'function') {
|
|
273
|
+
return await podLookupRepo.findByWebId(webidUrl);
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
catch (error) {
|
|
277
|
+
logger.warn(`WebID index lookup unavailable for ${username}: ${error}`);
|
|
278
|
+
}
|
|
279
|
+
return undefined;
|
|
280
|
+
}
|
|
281
|
+
async function tryFindPodByStorageSlug(podLookupRepo, username) {
|
|
282
|
+
try {
|
|
283
|
+
const pods = await podLookupRepo.listAllPods();
|
|
284
|
+
return pods.find((pod) => derivePodSlug(pod.baseUrl) === username);
|
|
285
|
+
}
|
|
286
|
+
catch (error) {
|
|
287
|
+
logger.warn(`Pod index lookup unavailable for ${username}: ${error}`);
|
|
288
|
+
return undefined;
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
function profileFromPod(username, webidUrl, pod) {
|
|
292
|
+
const storageUrl = ensureTrailingSlash(pod.baseUrl);
|
|
293
|
+
return {
|
|
294
|
+
username,
|
|
295
|
+
webidUrl,
|
|
296
|
+
storageUrl,
|
|
297
|
+
storageMode: 'cloud',
|
|
298
|
+
oidcIssuer: deriveOrigin(webidUrl),
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
function buildHostedWebIdUrl(username) {
|
|
302
|
+
return new URL(`${encodeURIComponent(username)}/profile/card#me`, getHostedIdentityBaseUrl()).toString();
|
|
303
|
+
}
|
|
304
|
+
function buildStorageWebIdUrl(storageUrl) {
|
|
305
|
+
return new URL('profile/card#me', ensureTrailingSlash(storageUrl)).toString();
|
|
306
|
+
}
|
|
307
|
+
function getHostedIdentityBaseUrl() {
|
|
308
|
+
return ensureTrailingSlash(absoluteHttpUrl(process.env.CSS_BASE_URL) ??
|
|
309
|
+
absoluteHttpUrl(process.env.BASE_URL) ??
|
|
310
|
+
'http://localhost:3000/');
|
|
311
|
+
}
|
|
312
|
+
function absoluteHttpUrl(value) {
|
|
313
|
+
if (!value) {
|
|
314
|
+
return undefined;
|
|
315
|
+
}
|
|
316
|
+
try {
|
|
317
|
+
const url = new URL(value);
|
|
318
|
+
return url.protocol === 'http:' || url.protocol === 'https:' ? url.toString() : undefined;
|
|
319
|
+
}
|
|
320
|
+
catch {
|
|
321
|
+
return undefined;
|
|
322
|
+
}
|
|
323
|
+
}
|
|
280
324
|
function ensureTrailingSlash(url) {
|
|
281
325
|
return url.replace(/\/+$/, '') + '/';
|
|
282
326
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WebIdProfileHandler.js","sourceRoot":"","sources":["../../../src/api/handlers/WebIdProfileHandler.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * WebID Profile API Handler\n *\n * 提供 WebID Profile 托管服务的 HTTP API\n *\n * GET /{username}/profile/card - 获取 WebID Profile (Turtle 格式)\n * POST /api/v1/identity/{username}/storage - 更新 storage 指针 (需认证)\n */\n\nimport type { ServerResponse, IncomingMessage } from 'node:http';\nimport { getLoggerFor } from 'global-logger-factory';\nimport type { ApiServer } from '../ApiServer';\nimport type { WebIdProfileRepository } from '../../identity/drizzle/WebIdProfileRepository';\nimport type { PodLookupRepository, PodLookupResult } from '../../identity/drizzle/PodLookupRepository';\n\nconst logger = getLoggerFor('WebIdProfileHandler');\n\nexport interface WebIdProfileHandlerOptions {\n profileRepo: WebIdProfileRepository;\n podLookupRepo?: PodLookupRepository;\n}\n\ninterface IdentityProfileResponse {\n username: string;\n webidUrl: string;\n storageUrl?: string;\n storageMode: 'cloud' | 'local' | 'custom';\n oidcIssuer?: string;\n createdAt?: Date;\n updatedAt?: Date;\n}\n\nexport function registerWebIdProfileRoutes(\n server: ApiServer,\n options: WebIdProfileHandlerOptions,\n): void {\n const { profileRepo } = options;\n\n /**\n * GET /{username}/profile/card\n *\n * 获取 WebID Profile (Turtle 格式)\n * 这是 Solid 标准的 WebID 端点\n */\n server.get('/:username/profile/card', async (_request, response, params) => {\n const username = decodeURIComponent(params.username);\n\n try {\n const profile = await resolveProfileWithStorageBackfill(username, options);\n\n if (!profile) {\n sendError(response, 404, 'Profile not found');\n return;\n }\n\n // 返回 Turtle 格式\n const turtle = profileRepo.generateProfileTurtle(profile);\n\n response.statusCode = 200;\n response.setHeader('Content-Type', 'text/turtle');\n response.setHeader('Link', `<${profile.webidUrl}>; rel=\"describedby\"`);\n response.end(turtle);\n } catch (error) {\n logger.error(`Failed to get profile for ${username}: ${error}`);\n sendError(response, 500, 'Internal server error');\n }\n }, { public: true });\n\n /**\n * POST /api/v1/identity/{username}/storage\n *\n * 更新 storage 指针\n * 用于 Local 节点更新其 storage URL\n *\n * Request body:\n * {\n * \"storageUrl\": \"https://alice.undefineds.xyz/\"\n * }\n */\n server.post('/api/v1/identity/:username/storage', async (request, response, params) => {\n const username = decodeURIComponent(params.username);\n\n try {\n const body = await readJsonBody(request);\n const payload = body as { storageUrl?: string; storageMode?: string } | undefined;\n\n if (!payload?.storageUrl) {\n sendError(response, 400, 'storageUrl is required');\n return;\n }\n\n // 验证 URL 格式\n try {\n new URL(payload.storageUrl);\n } catch {\n sendError(response, 400, 'Invalid storageUrl format');\n return;\n }\n\n const profile = await profileRepo.updateStorage(username, {\n storageUrl: payload.storageUrl,\n storageMode: payload.storageMode as 'cloud' | 'local' | 'custom' | undefined,\n });\n\n if (!profile) {\n sendError(response, 404, 'Profile not found');\n return;\n }\n\n logger.info(`Updated storage for ${username}: ${payload.storageUrl}`);\n\n sendJson(response, 200, {\n success: true,\n username,\n storageUrl: profile.storageUrl,\n storageMode: profile.storageMode,\n updatedAt: profile.updatedAt.toISOString(),\n });\n } catch (error) {\n logger.error(`Failed to update storage for ${username}: ${error}`);\n sendError(response, 500, 'Internal server error');\n }\n });\n\n /**\n * GET /api/v1/identity/{username}\n *\n * 获取 WebID Profile 信息 (JSON 格式)\n */\n server.get('/api/v1/identity/:username', async (_request, response, params) => {\n const username = decodeURIComponent(params.username);\n\n try {\n const profile = await resolveIdentityLookup(username, options);\n\n if (!profile) {\n sendError(response, 404, 'Profile not found');\n return;\n }\n\n const body: Record<string, unknown> = {\n username: profile.username,\n webidUrl: profile.webidUrl,\n storageUrl: profile.storageUrl,\n storageMode: profile.storageMode,\n oidcIssuer: profile.oidcIssuer,\n };\n if (profile.createdAt) {\n body.createdAt = profile.createdAt.toISOString();\n }\n if (profile.updatedAt) {\n body.updatedAt = profile.updatedAt.toISOString();\n }\n sendJson(response, 200, body);\n } catch (error) {\n logger.error(`Failed to get profile for ${username}: ${error}`);\n sendError(response, 500, 'Internal server error');\n }\n }, { public: true });\n\n /**\n * POST /api/v1/identity\n *\n * 创建 WebID Profile\n *\n * Request body:\n * {\n * \"username\": \"alice\",\n * \"storageMode\": \"local\", // optional, default: \"cloud\"\n * \"storageUrl\": \"https://alice.undefineds.xyz/\" // optional\n * }\n */\n server.post('/api/v1/identity', async (request, response, _params) => {\n try {\n const body = await readJsonBody(request);\n const payload = body as {\n username?: string;\n storageMode?: string;\n storageUrl?: string;\n accountId?: string;\n } | undefined;\n\n if (!payload?.username) {\n sendError(response, 400, 'username is required');\n return;\n }\n\n // 验证用户名格式\n if (!/^[a-z0-9][a-z0-9-]{1,61}[a-z0-9]$/.test(payload.username)) {\n sendError(response, 400, 'Invalid username format');\n return;\n }\n\n // 检查是否已存在\n const existing = await profileRepo.get(payload.username);\n if (existing) {\n sendError(response, 409, 'Username already taken');\n return;\n }\n\n const profile = await profileRepo.create({\n username: payload.username,\n storageMode: payload.storageMode as 'cloud' | 'local' | 'custom' | undefined,\n storageUrl: payload.storageUrl,\n accountId: payload.accountId,\n });\n\n logger.info(`Created profile for ${payload.username}`);\n\n sendJson(response, 201, {\n success: true,\n username: profile.username,\n webidUrl: profile.webidUrl,\n storageUrl: profile.storageUrl,\n storageMode: profile.storageMode,\n createdAt: profile.createdAt.toISOString(),\n });\n } catch (error) {\n logger.error(`Failed to create profile: ${error}`);\n sendError(response, 500, 'Internal server error');\n }\n });\n\n logger.info('WebID Profile routes registered');\n}\n\nasync function resolveIdentityLookup(\n username: string,\n options: WebIdProfileHandlerOptions,\n): Promise<IdentityProfileResponse | null> {\n try {\n const profile = await resolveProfileWithStorageBackfill(username, options);\n if (profile) {\n return profile;\n }\n } catch (error) {\n logger.warn(`Profile lookup unavailable for ${username}, falling back to Pod index: ${error}`);\n }\n\n return resolveProfileFromPods(username, options);\n}\n\nasync function resolveProfileFromPods(\n username: string,\n options: WebIdProfileHandlerOptions,\n): Promise<IdentityProfileResponse | null> {\n const { podLookupRepo } = options;\n if (!podLookupRepo) {\n return null;\n }\n\n let pods: PodLookupResult[];\n try {\n pods = await podLookupRepo.listAllPods();\n } catch (error) {\n logger.warn(`Pod index lookup unavailable for ${username}: ${error}`);\n return null;\n }\n\n const match = pods.find((pod) => derivePodSlug(pod.baseUrl) === username);\n if (!match) {\n return null;\n }\n\n const storageUrl = ensureTrailingSlash(match.baseUrl);\n return {\n username,\n webidUrl: `${storageUrl}profile/card#me`,\n storageUrl,\n storageMode: 'cloud',\n oidcIssuer: deriveOrigin(storageUrl),\n };\n}\n\nasync function resolveProfileWithStorageBackfill(\n username: string,\n options: WebIdProfileHandlerOptions,\n) {\n const { profileRepo, podLookupRepo } = options;\n const profile = await profileRepo.get(username);\n if (!profile) {\n return null;\n }\n\n if (profile.storageUrl || !profile.accountId || !podLookupRepo) {\n return profile;\n }\n\n let pods: PodLookupResult[];\n try {\n pods = await podLookupRepo.listByAccountId(profile.accountId);\n } catch (error) {\n logger.warn(`Skipped storage backfill for ${username}: pod index unavailable for account ${profile.accountId}: ${error}`);\n return profile;\n }\n const storageUrl = selectStorageBackfillCandidate(username, pods);\n if (!storageUrl) {\n logger.warn(`Skipped storage backfill for ${username}: no unambiguous pod found for account ${profile.accountId}`);\n return profile;\n }\n\n try {\n const updated = await profileRepo.updateStorage(username, {\n storageUrl,\n storageMode: profile.storageMode,\n });\n if (updated) {\n logger.info(`Backfilled storage for ${username}: ${storageUrl}`);\n return updated;\n }\n } catch (error) {\n logger.warn(`Failed to backfill storage for ${username}: ${error}`);\n }\n\n return profile;\n}\n\nfunction selectStorageBackfillCandidate(\n username: string,\n pods: PodLookupResult[],\n): string | null {\n if (pods.length === 0) {\n return null;\n }\n\n const exactMatches = pods.filter((pod) => derivePodSlug(pod.baseUrl) === username);\n if (exactMatches.length === 1) {\n return ensureTrailingSlash(exactMatches[0].baseUrl);\n }\n\n if (exactMatches.length > 1) {\n return null;\n }\n\n if (pods.length === 1) {\n return ensureTrailingSlash(pods[0].baseUrl);\n }\n\n return null;\n}\n\nfunction derivePodSlug(baseUrl: string): string | null {\n try {\n const parsed = new URL(baseUrl);\n const [slug] = parsed.pathname.split('/').filter(Boolean);\n return slug || null;\n } catch {\n return null;\n }\n}\n\nfunction ensureTrailingSlash(url: string): string {\n return url.replace(/\\/+$/, '') + '/';\n}\n\nfunction deriveOrigin(url: string): string | undefined {\n try {\n return ensureTrailingSlash(new URL(url).origin);\n } catch {\n return undefined;\n }\n}\n\nasync function readJsonBody(request: IncomingMessage): 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\nfunction sendError(response: ServerResponse, status: number, message: string): void {\n sendJson(response, status, { error: message });\n}\n"]}
|
|
1
|
+
{"version":3,"file":"WebIdProfileHandler.js","sourceRoot":"","sources":["../../../src/api/handlers/WebIdProfileHandler.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * WebID Profile API Handler\n *\n * 提供 WebID Profile 托管服务的 HTTP API\n *\n * GET /{username}/profile/card - 获取 WebID Profile (Turtle 格式)\n * POST /api/v1/identity/{username}/storage - 更新 storage 指针 (需认证)\n */\n\nimport type { ServerResponse, IncomingMessage } from 'node:http';\nimport { getLoggerFor } from 'global-logger-factory';\nimport type { ApiServer } from '../ApiServer';\nimport type { WebIdProfileRepository } from '../../identity/drizzle/WebIdProfileRepository';\nimport type { PodLookupRepository, PodLookupResult } from '../../identity/drizzle/PodLookupRepository';\n\nconst logger = getLoggerFor('WebIdProfileHandler');\n\nexport interface WebIdProfileHandlerOptions {\n profileRepo: WebIdProfileRepository;\n podLookupRepo?: PodLookupRepository;\n}\n\ninterface IdentityProfileResponse {\n username: string;\n webidUrl: string;\n storageUrl?: string;\n storageMode: 'cloud' | 'local' | 'custom';\n oidcIssuer?: string;\n createdAt?: Date;\n updatedAt?: Date;\n}\n\nexport function registerWebIdProfileRoutes(\n server: ApiServer,\n options: WebIdProfileHandlerOptions,\n): void {\n const { profileRepo } = options;\n\n /**\n * GET /{username}/profile/card\n *\n * 获取 WebID Profile (Turtle 格式)\n * 这是 Solid 标准的 WebID 端点\n */\n server.get('/:username/profile/card', async (_request, response, params) => {\n const username = decodeURIComponent(params.username);\n\n try {\n const profile = await resolveIdentityLookup(username, options);\n\n if (!profile) {\n sendError(response, 404, 'Profile not found');\n return;\n }\n\n // 返回 Turtle 格式\n const turtle = profileRepo.generateProfileTurtle(profile);\n\n response.statusCode = 200;\n response.setHeader('Content-Type', 'text/turtle');\n response.setHeader('Link', `<${profile.webidUrl}>; rel=\"describedby\"`);\n response.end(turtle);\n } catch (error) {\n logger.error(`Failed to get profile for ${username}: ${error}`);\n sendError(response, 500, 'Internal server error');\n }\n }, { public: true });\n\n /**\n * POST /api/v1/identity/{username}/storage\n *\n * 更新 storage 指针\n * 用于 Local 节点更新其 storage URL\n *\n * Request body:\n * {\n * \"storageUrl\": \"https://alice.undefineds.xyz/\"\n * }\n */\n server.post('/api/v1/identity/:username/storage', async (request, response, params) => {\n const username = decodeURIComponent(params.username);\n\n try {\n const body = await readJsonBody(request);\n const payload = body as { storageUrl?: string; storageMode?: string } | undefined;\n\n if (!payload?.storageUrl) {\n sendError(response, 400, 'storageUrl is required');\n return;\n }\n\n // 验证 URL 格式\n try {\n new URL(payload.storageUrl);\n } catch {\n sendError(response, 400, 'Invalid storageUrl format');\n return;\n }\n\n const profile = await profileRepo.updateStorage(username, {\n storageUrl: payload.storageUrl,\n storageMode: payload.storageMode as 'cloud' | 'local' | 'custom' | undefined,\n });\n\n if (!profile) {\n sendError(response, 404, 'Profile not found');\n return;\n }\n\n logger.info(`Updated storage for ${username}: ${payload.storageUrl}`);\n\n sendJson(response, 200, {\n success: true,\n username,\n storageUrl: profile.storageUrl,\n storageMode: profile.storageMode,\n updatedAt: profile.updatedAt.toISOString(),\n });\n } catch (error) {\n logger.error(`Failed to update storage for ${username}: ${error}`);\n sendError(response, 500, 'Internal server error');\n }\n });\n\n /**\n * GET /api/v1/identity/{username}\n *\n * 获取 WebID Profile 信息 (JSON 格式)\n */\n server.get('/api/v1/identity/:username', async (_request, response, params) => {\n const username = decodeURIComponent(params.username);\n\n try {\n const profile = await resolveIdentityLookup(username, options);\n\n if (!profile) {\n sendError(response, 404, 'Profile not found');\n return;\n }\n\n const body: Record<string, unknown> = {\n username: profile.username,\n webidUrl: profile.webidUrl,\n storageUrl: profile.storageUrl,\n storageMode: profile.storageMode,\n oidcIssuer: profile.oidcIssuer,\n };\n if (profile.createdAt) {\n body.createdAt = profile.createdAt.toISOString();\n }\n if (profile.updatedAt) {\n body.updatedAt = profile.updatedAt.toISOString();\n }\n sendJson(response, 200, body);\n } catch (error) {\n logger.error(`Failed to get profile for ${username}: ${error}`);\n sendError(response, 500, 'Internal server error');\n }\n }, { public: true });\n\n /**\n * POST /api/v1/identity\n *\n * 创建 WebID Profile\n *\n * Request body:\n * {\n * \"username\": \"alice\",\n * \"storageMode\": \"local\", // optional, default: \"cloud\"\n * \"storageUrl\": \"https://alice.undefineds.xyz/\" // optional\n * }\n */\n server.post('/api/v1/identity', async (request, response, _params) => {\n try {\n const body = await readJsonBody(request);\n const payload = body as {\n username?: string;\n storageMode?: string;\n storageUrl?: string;\n accountId?: string;\n } | undefined;\n\n if (!payload?.username) {\n sendError(response, 400, 'username is required');\n return;\n }\n\n // 验证用户名格式\n if (!/^[a-z0-9][a-z0-9-]{1,61}[a-z0-9]$/.test(payload.username)) {\n sendError(response, 400, 'Invalid username format');\n return;\n }\n\n // 检查是否已存在\n const existing = await profileRepo.get(payload.username);\n if (existing) {\n sendError(response, 409, 'Username already taken');\n return;\n }\n\n const profile = await profileRepo.create({\n username: payload.username,\n storageMode: payload.storageMode as 'cloud' | 'local' | 'custom' | undefined,\n storageUrl: payload.storageUrl,\n accountId: payload.accountId,\n });\n\n logger.info(`Created profile for ${payload.username}`);\n\n sendJson(response, 201, {\n success: true,\n username: profile.username,\n webidUrl: profile.webidUrl,\n storageUrl: profile.storageUrl,\n storageMode: profile.storageMode,\n createdAt: profile.createdAt.toISOString(),\n });\n } catch (error) {\n logger.error(`Failed to create profile: ${error}`);\n sendError(response, 500, 'Internal server error');\n }\n });\n\n logger.info('WebID Profile routes registered');\n}\n\nasync function resolveIdentityLookup(\n username: string,\n options: WebIdProfileHandlerOptions,\n): Promise<IdentityProfileResponse | null> {\n try {\n const profile = await resolveProfileWithStorageBackfill(username, options);\n if (profile) {\n return profile;\n }\n } catch (error) {\n logger.warn(`Profile lookup unavailable for ${username}, falling back to Pod index: ${error}`);\n }\n\n return resolveProfileFromPods(username, options);\n}\n\nasync function resolveProfileFromPods(\n username: string,\n options: WebIdProfileHandlerOptions,\n): Promise<IdentityProfileResponse | null> {\n const { podLookupRepo } = options;\n if (!podLookupRepo) {\n return null;\n }\n\n const webidUrl = buildHostedWebIdUrl(username);\n const webIdMatch = await tryFindPodByWebId(podLookupRepo, webidUrl, username);\n if (webIdMatch) {\n return profileFromPod(username, webidUrl, webIdMatch);\n }\n\n const match = await tryFindPodByStorageSlug(podLookupRepo, username);\n if (!match) {\n return null;\n }\n\n return profileFromPod(username, buildStorageWebIdUrl(match.baseUrl), match);\n}\n\nasync function resolveProfileWithStorageBackfill(\n username: string,\n options: WebIdProfileHandlerOptions,\n) {\n const { profileRepo, podLookupRepo } = options;\n const profile = await profileRepo.get(username);\n if (!profile) {\n return null;\n }\n\n if (profile.storageUrl || !profile.accountId || !podLookupRepo) {\n return profile;\n }\n\n let pods: PodLookupResult[];\n try {\n pods = await podLookupRepo.listByAccountId(profile.accountId);\n } catch (error) {\n logger.warn(`Skipped storage backfill for ${username}: pod index unavailable for account ${profile.accountId}: ${error}`);\n return profile;\n }\n const storageUrl = selectStorageBackfillCandidate(username, pods);\n if (!storageUrl) {\n logger.warn(`Skipped storage backfill for ${username}: no unambiguous pod found for account ${profile.accountId}`);\n return profile;\n }\n\n try {\n const updated = await profileRepo.updateStorage(username, {\n storageUrl,\n storageMode: profile.storageMode,\n });\n if (updated) {\n logger.info(`Backfilled storage for ${username}: ${storageUrl}`);\n return updated;\n }\n } catch (error) {\n logger.warn(`Failed to backfill storage for ${username}: ${error}`);\n }\n\n return profile;\n}\n\nfunction selectStorageBackfillCandidate(\n username: string,\n pods: PodLookupResult[],\n): string | null {\n if (pods.length === 0) {\n return null;\n }\n\n const exactMatches = pods.filter((pod) => derivePodSlug(pod.baseUrl) === username);\n if (exactMatches.length === 1) {\n return ensureTrailingSlash(exactMatches[0].baseUrl);\n }\n\n if (exactMatches.length > 1) {\n return null;\n }\n\n if (pods.length === 1) {\n return ensureTrailingSlash(pods[0].baseUrl);\n }\n\n return null;\n}\n\nfunction derivePodSlug(baseUrl: string): string | null {\n try {\n const parsed = new URL(baseUrl);\n const [slug] = parsed.pathname.split('/').filter(Boolean);\n return slug || null;\n } catch {\n return null;\n }\n}\n\nasync function tryFindPodByWebId(\n podLookupRepo: PodLookupRepository,\n webidUrl: string,\n username: string,\n): Promise<PodLookupResult | undefined> {\n try {\n if (typeof podLookupRepo.findByWebId === 'function') {\n return await podLookupRepo.findByWebId(webidUrl);\n }\n } catch (error) {\n logger.warn(`WebID index lookup unavailable for ${username}: ${error}`);\n }\n return undefined;\n}\n\nasync function tryFindPodByStorageSlug(\n podLookupRepo: PodLookupRepository,\n username: string,\n): Promise<PodLookupResult | undefined> {\n try {\n const pods = await podLookupRepo.listAllPods();\n return pods.find((pod) => derivePodSlug(pod.baseUrl) === username);\n } catch (error) {\n logger.warn(`Pod index lookup unavailable for ${username}: ${error}`);\n return undefined;\n }\n}\n\nfunction profileFromPod(\n username: string,\n webidUrl: string,\n pod: PodLookupResult,\n): IdentityProfileResponse {\n const storageUrl = ensureTrailingSlash(pod.baseUrl);\n return {\n username,\n webidUrl,\n storageUrl,\n storageMode: 'cloud',\n oidcIssuer: deriveOrigin(webidUrl),\n };\n}\n\nfunction buildHostedWebIdUrl(username: string): string {\n return new URL(`${encodeURIComponent(username)}/profile/card#me`, getHostedIdentityBaseUrl()).toString();\n}\n\nfunction buildStorageWebIdUrl(storageUrl: string): string {\n return new URL('profile/card#me', ensureTrailingSlash(storageUrl)).toString();\n}\n\nfunction getHostedIdentityBaseUrl(): string {\n return ensureTrailingSlash(\n absoluteHttpUrl(process.env.CSS_BASE_URL) ??\n absoluteHttpUrl(process.env.BASE_URL) ??\n 'http://localhost:3000/',\n );\n}\n\nfunction absoluteHttpUrl(value: string | undefined): string | undefined {\n if (!value) {\n return undefined;\n }\n try {\n const url = new URL(value);\n return url.protocol === 'http:' || url.protocol === 'https:' ? url.toString() : undefined;\n } catch {\n return undefined;\n }\n}\n\nfunction ensureTrailingSlash(url: string): string {\n return url.replace(/\\/+$/, '') + '/';\n}\n\nfunction deriveOrigin(url: string): string | undefined {\n try {\n return ensureTrailingSlash(new URL(url).origin);\n } catch {\n return undefined;\n }\n}\n\nasync function readJsonBody(request: IncomingMessage): 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\nfunction sendError(response: ServerResponse, status: number, message: string): void {\n sendJson(response, status, { error: message });\n}\n"]}
|
|
@@ -3,6 +3,7 @@ export interface PodLookupResult {
|
|
|
3
3
|
podId: string;
|
|
4
4
|
accountId: string;
|
|
5
5
|
baseUrl: string;
|
|
6
|
+
webId?: string;
|
|
6
7
|
nodeId?: string;
|
|
7
8
|
edgeNodeId?: string;
|
|
8
9
|
}
|
|
@@ -33,6 +34,14 @@ export declare class PodLookupRepository {
|
|
|
33
34
|
* Get Pod by ID.
|
|
34
35
|
*/
|
|
35
36
|
findById(podId: string): Promise<PodLookupResult | undefined>;
|
|
37
|
+
/**
|
|
38
|
+
* Find Pod by a linked WebID URL.
|
|
39
|
+
*
|
|
40
|
+
* CSS account data stores WebID links separately from Pod base URLs. This is
|
|
41
|
+
* the precise lookup for IdP/SP split deployments where the WebID path does
|
|
42
|
+
* not have to match the storage base URL.
|
|
43
|
+
*/
|
|
44
|
+
findByWebId(webId: string): Promise<PodLookupResult | undefined>;
|
|
36
45
|
/**
|
|
37
46
|
* List Pods for a specific account.
|
|
38
47
|
*/
|
|
@@ -38,6 +38,21 @@ class PodLookupRepository {
|
|
|
38
38
|
const pods = await this.getAllPods();
|
|
39
39
|
return pods.find((p) => p.podId === podId);
|
|
40
40
|
}
|
|
41
|
+
/**
|
|
42
|
+
* Find Pod by a linked WebID URL.
|
|
43
|
+
*
|
|
44
|
+
* CSS account data stores WebID links separately from Pod base URLs. This is
|
|
45
|
+
* the precise lookup for IdP/SP split deployments where the WebID path does
|
|
46
|
+
* not have to match the storage base URL.
|
|
47
|
+
*/
|
|
48
|
+
async findByWebId(webId) {
|
|
49
|
+
const normalized = normalizeWebId(webId);
|
|
50
|
+
if (!normalized) {
|
|
51
|
+
return undefined;
|
|
52
|
+
}
|
|
53
|
+
const pods = await this.getAllPods();
|
|
54
|
+
return pods.find((pod) => normalizeWebId(pod.webId) === normalized);
|
|
55
|
+
}
|
|
41
56
|
/**
|
|
42
57
|
* List Pods for a specific account.
|
|
43
58
|
*/
|
|
@@ -136,13 +151,20 @@ class PodLookupRepository {
|
|
|
136
151
|
const accountId = row.key.replace('accounts/data/', '');
|
|
137
152
|
const data = typeof row.value === 'string' ? JSON.parse(row.value) : row.value;
|
|
138
153
|
const podMap = data['**pod**'] || data.pod || {};
|
|
154
|
+
const webIds = extractAccountWebIds(data);
|
|
139
155
|
for (const [podId, podData] of Object.entries(podMap)) {
|
|
140
156
|
const pod = podData;
|
|
141
157
|
if (pod.baseUrl && typeof pod.baseUrl === 'string') {
|
|
158
|
+
const podWebIds = [
|
|
159
|
+
typeof pod.webId === 'string' ? pod.webId : undefined,
|
|
160
|
+
...extractPodOwnerWebIds(pod),
|
|
161
|
+
...webIds,
|
|
162
|
+
].filter((value) => typeof value === 'string');
|
|
142
163
|
pods.push({
|
|
143
164
|
podId,
|
|
144
165
|
accountId,
|
|
145
166
|
baseUrl: pod.baseUrl,
|
|
167
|
+
webId: dedupeStrings(podWebIds)[0],
|
|
146
168
|
nodeId: typeof pod.nodeId === 'string' ? pod.nodeId : undefined,
|
|
147
169
|
edgeNodeId: typeof pod.edgeNodeId === 'string' ? pod.edgeNodeId : undefined,
|
|
148
170
|
});
|
|
@@ -157,4 +179,52 @@ class PodLookupRepository {
|
|
|
157
179
|
}
|
|
158
180
|
}
|
|
159
181
|
exports.PodLookupRepository = PodLookupRepository;
|
|
182
|
+
function extractAccountWebIds(data) {
|
|
183
|
+
if (!data || typeof data !== 'object') {
|
|
184
|
+
return [];
|
|
185
|
+
}
|
|
186
|
+
const record = data;
|
|
187
|
+
const linkMap = record['**webIdLink**'] || record.webIdLink || {};
|
|
188
|
+
if (!linkMap || typeof linkMap !== 'object') {
|
|
189
|
+
return [];
|
|
190
|
+
}
|
|
191
|
+
return Object.values(linkMap)
|
|
192
|
+
.map((value) => {
|
|
193
|
+
if (!value || typeof value !== 'object') {
|
|
194
|
+
return undefined;
|
|
195
|
+
}
|
|
196
|
+
const webId = value.webId;
|
|
197
|
+
return typeof webId === 'string' ? webId : undefined;
|
|
198
|
+
})
|
|
199
|
+
.filter((value) => typeof value === 'string');
|
|
200
|
+
}
|
|
201
|
+
function extractPodOwnerWebIds(pod) {
|
|
202
|
+
const ownerMap = pod['**owner**'] || pod.owner || {};
|
|
203
|
+
if (!ownerMap || typeof ownerMap !== 'object') {
|
|
204
|
+
return [];
|
|
205
|
+
}
|
|
206
|
+
return Object.values(ownerMap)
|
|
207
|
+
.map((value) => {
|
|
208
|
+
if (!value || typeof value !== 'object') {
|
|
209
|
+
return undefined;
|
|
210
|
+
}
|
|
211
|
+
const webId = value.webId;
|
|
212
|
+
return typeof webId === 'string' ? webId : undefined;
|
|
213
|
+
})
|
|
214
|
+
.filter((value) => typeof value === 'string');
|
|
215
|
+
}
|
|
216
|
+
function normalizeWebId(webId) {
|
|
217
|
+
if (!webId) {
|
|
218
|
+
return undefined;
|
|
219
|
+
}
|
|
220
|
+
try {
|
|
221
|
+
return new URL(webId).toString();
|
|
222
|
+
}
|
|
223
|
+
catch {
|
|
224
|
+
return webId;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
function dedupeStrings(values) {
|
|
228
|
+
return [...new Set(values)];
|
|
229
|
+
}
|
|
160
230
|
//# sourceMappingURL=PodLookupRepository.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PodLookupRepository.js","sourceRoot":"","sources":["../../../src/identity/drizzle/PodLookupRepository.ts"],"names":[],"mappings":"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","sourcesContent":["import { sql } from 'drizzle-orm';\nimport { type IdentityDatabase, executeQuery, executeStatement } from './db';\n\nexport interface PodLookupResult {\n podId: string;\n accountId: string;\n baseUrl: string;\n nodeId?: string;\n edgeNodeId?: string;\n}\n\nexport interface PodMigrationStatus {\n podId: string;\n nodeId?: string;\n migrationStatus?: 'syncing' | 'done' | null;\n migrationTargetNode?: string;\n migrationProgress?: number;\n}\n\ninterface InternalKvRow {\n key?: string;\n value?: string;\n id?: string;\n account_id?: string;\n base_url?: string;\n node_id?: string;\n edge_node_id?: string;\n}\n\n/**\n * Repository for Pod lookup operations.\n *\n * Reads Pod data from CSS's internal_kv table where account data is stored.\n * CSS stores account data at key \"accounts/data/{accountId}\" with Pod info\n * nested in the \"**pod**\" field.\n */\nexport class PodLookupRepository {\n private readonly kvTableName: string;\n private readonly usageTableName: string;\n\n public constructor(\n private readonly db: IdentityDatabase,\n kvTableName?: string,\n ) {\n this.kvTableName = kvTableName ?? 'internal_kv';\n this.usageTableName = 'identity_pod_usage';\n }\n\n /**\n * Find Pod by resource path (matches longest baseUrl prefix).\n */\n public async findByResourceIdentifier(resourcePath: string): Promise<PodLookupResult | undefined> {\n const pods = await this.getAllPods();\n\n let bestMatch: PodLookupResult | undefined;\n let bestLength = 0;\n\n for (const pod of pods) {\n if (resourcePath.startsWith(pod.baseUrl) && pod.baseUrl.length > bestLength) {\n bestMatch = pod;\n bestLength = pod.baseUrl.length;\n }\n }\n\n return bestMatch;\n }\n\n /**\n * Get Pod by ID.\n */\n public async findById(podId: string): Promise<PodLookupResult | undefined> {\n const pods = await this.getAllPods();\n return pods.find((p) => p.podId === podId);\n }\n\n /**\n * List Pods for a specific account.\n */\n public async listByAccountId(accountId: string): Promise<PodLookupResult[]> {\n const pods = await this.getAllPods();\n return pods.filter((pod) => pod.accountId === accountId);\n }\n\n /**\n * Get migration status for a Pod from identity_pod_usage table.\n */\n public async getMigrationStatus(podId: string): Promise<PodMigrationStatus | undefined> {\n try {\n const tableId = sql.identifier([this.usageTableName]);\n const result = await executeQuery<{\n pod_id?: string;\n id?: string;\n node_id?: string | null;\n migration_status?: string | null;\n migration_target_node?: string | null;\n migration_progress?: number | null;\n }>(this.db, sql`\n SELECT pod_id, node_id, migration_status, migration_target_node, migration_progress\n FROM ${tableId}\n WHERE pod_id = ${podId}\n LIMIT 1\n `);\n\n if (result.rows.length === 0) {\n return undefined;\n }\n const row = result.rows[0];\n return {\n podId: row.pod_id ?? row.id ?? podId,\n nodeId: row.node_id ?? undefined,\n migrationStatus: row.migration_status as 'syncing' | 'done' | null | undefined,\n migrationTargetNode: row.migration_target_node ?? undefined,\n migrationProgress: row.migration_progress ?? undefined,\n };\n } catch {\n // Table might not exist.\n return undefined;\n }\n }\n\n /**\n * Set the nodeId for a Pod in identity_pod_usage table.\n */\n public async setNodeId(podId: string, nodeId: string): Promise<void> {\n const tableId = sql.identifier([this.usageTableName]);\n await executeStatement(this.db, sql`\n INSERT INTO ${tableId} (pod_id, account_id, node_id)\n VALUES (${podId}, '', ${nodeId})\n ON CONFLICT (pod_id) DO UPDATE SET node_id = ${nodeId}\n `);\n }\n\n /**\n * Update migration status for a Pod in identity_pod_usage table.\n */\n public async setMigrationStatus(\n podId: string,\n status: 'syncing' | 'done' | null,\n targetNode?: string | null,\n progress?: number | null,\n ): Promise<void> {\n const tableId = sql.identifier([this.usageTableName]);\n await executeStatement(this.db, sql`\n INSERT INTO ${tableId} (pod_id, account_id, migration_status, migration_target_node, migration_progress)\n VALUES (${podId}, '', ${status}, ${targetNode ?? null}, ${progress ?? 0})\n ON CONFLICT (pod_id) DO UPDATE SET\n migration_status = ${status},\n migration_target_node = ${targetNode ?? null},\n migration_progress = ${progress ?? 0}\n `);\n }\n\n /**\n * List all pods.\n */\n public async listAllPods(): Promise<PodLookupResult[]> {\n return this.getAllPods();\n }\n\n /**\n * Extract all pods from CSS's internal_kv storage.\n *\n * It keeps backward compatibility with legacy rows that already expose\n * id/account_id/base_url columns (used by some unit tests and older schemas).\n */\n private async getAllPods(): Promise<PodLookupResult[]> {\n const kvTableId = sql.identifier([this.kvTableName]);\n\n const result = await executeQuery<InternalKvRow>(this.db, sql`\n SELECT key, value FROM ${kvTableId}\n WHERE key LIKE 'accounts/data/%'\n `);\n\n const pods: PodLookupResult[] = [];\n\n for (const row of result.rows) {\n if (row.id && row.account_id && row.base_url) {\n pods.push({\n podId: String(row.id),\n accountId: String(row.account_id),\n baseUrl: String(row.base_url),\n nodeId: row.node_id ? String(row.node_id) : undefined,\n edgeNodeId: row.edge_node_id ? String(row.edge_node_id) : undefined,\n });\n continue;\n }\n\n if (!row.key || row.value === undefined) {\n continue;\n }\n\n try {\n const accountId = row.key.replace('accounts/data/', '');\n const data = typeof row.value === 'string' ? JSON.parse(row.value) : row.value;\n\n const podMap = (data as any)['**pod**'] || (data as any).pod || {};\n\n for (const [podId, podData] of Object.entries(podMap)) {\n const pod = podData as Record<string, unknown>;\n if (pod.baseUrl && typeof pod.baseUrl === 'string') {\n pods.push({\n podId,\n accountId,\n baseUrl: pod.baseUrl,\n nodeId: typeof pod.nodeId === 'string' ? pod.nodeId : undefined,\n edgeNodeId: typeof pod.edgeNodeId === 'string' ? pod.edgeNodeId : undefined,\n });\n }\n }\n } catch {\n // Skip malformed entries.\n }\n }\n\n return pods;\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"PodLookupRepository.js","sourceRoot":"","sources":["../../../src/identity/drizzle/PodLookupRepository.ts"],"names":[],"mappings":"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","sourcesContent":["import { sql } from 'drizzle-orm';\nimport { type IdentityDatabase, executeQuery, executeStatement } from './db';\n\nexport interface PodLookupResult {\n podId: string;\n accountId: string;\n baseUrl: string;\n webId?: string;\n nodeId?: string;\n edgeNodeId?: string;\n}\n\nexport interface PodMigrationStatus {\n podId: string;\n nodeId?: string;\n migrationStatus?: 'syncing' | 'done' | null;\n migrationTargetNode?: string;\n migrationProgress?: number;\n}\n\ninterface InternalKvRow {\n key?: string;\n value?: string;\n id?: string;\n account_id?: string;\n base_url?: string;\n node_id?: string;\n edge_node_id?: string;\n}\n\n/**\n * Repository for Pod lookup operations.\n *\n * Reads Pod data from CSS's internal_kv table where account data is stored.\n * CSS stores account data at key \"accounts/data/{accountId}\" with Pod info\n * nested in the \"**pod**\" field.\n */\nexport class PodLookupRepository {\n private readonly kvTableName: string;\n private readonly usageTableName: string;\n\n public constructor(\n private readonly db: IdentityDatabase,\n kvTableName?: string,\n ) {\n this.kvTableName = kvTableName ?? 'internal_kv';\n this.usageTableName = 'identity_pod_usage';\n }\n\n /**\n * Find Pod by resource path (matches longest baseUrl prefix).\n */\n public async findByResourceIdentifier(resourcePath: string): Promise<PodLookupResult | undefined> {\n const pods = await this.getAllPods();\n\n let bestMatch: PodLookupResult | undefined;\n let bestLength = 0;\n\n for (const pod of pods) {\n if (resourcePath.startsWith(pod.baseUrl) && pod.baseUrl.length > bestLength) {\n bestMatch = pod;\n bestLength = pod.baseUrl.length;\n }\n }\n\n return bestMatch;\n }\n\n /**\n * Get Pod by ID.\n */\n public async findById(podId: string): Promise<PodLookupResult | undefined> {\n const pods = await this.getAllPods();\n return pods.find((p) => p.podId === podId);\n }\n\n /**\n * Find Pod by a linked WebID URL.\n *\n * CSS account data stores WebID links separately from Pod base URLs. This is\n * the precise lookup for IdP/SP split deployments where the WebID path does\n * not have to match the storage base URL.\n */\n public async findByWebId(webId: string): Promise<PodLookupResult | undefined> {\n const normalized = normalizeWebId(webId);\n if (!normalized) {\n return undefined;\n }\n\n const pods = await this.getAllPods();\n return pods.find((pod) => normalizeWebId(pod.webId) === normalized);\n }\n\n /**\n * List Pods for a specific account.\n */\n public async listByAccountId(accountId: string): Promise<PodLookupResult[]> {\n const pods = await this.getAllPods();\n return pods.filter((pod) => pod.accountId === accountId);\n }\n\n /**\n * Get migration status for a Pod from identity_pod_usage table.\n */\n public async getMigrationStatus(podId: string): Promise<PodMigrationStatus | undefined> {\n try {\n const tableId = sql.identifier([this.usageTableName]);\n const result = await executeQuery<{\n pod_id?: string;\n id?: string;\n node_id?: string | null;\n migration_status?: string | null;\n migration_target_node?: string | null;\n migration_progress?: number | null;\n }>(this.db, sql`\n SELECT pod_id, node_id, migration_status, migration_target_node, migration_progress\n FROM ${tableId}\n WHERE pod_id = ${podId}\n LIMIT 1\n `);\n\n if (result.rows.length === 0) {\n return undefined;\n }\n const row = result.rows[0];\n return {\n podId: row.pod_id ?? row.id ?? podId,\n nodeId: row.node_id ?? undefined,\n migrationStatus: row.migration_status as 'syncing' | 'done' | null | undefined,\n migrationTargetNode: row.migration_target_node ?? undefined,\n migrationProgress: row.migration_progress ?? undefined,\n };\n } catch {\n // Table might not exist.\n return undefined;\n }\n }\n\n /**\n * Set the nodeId for a Pod in identity_pod_usage table.\n */\n public async setNodeId(podId: string, nodeId: string): Promise<void> {\n const tableId = sql.identifier([this.usageTableName]);\n await executeStatement(this.db, sql`\n INSERT INTO ${tableId} (pod_id, account_id, node_id)\n VALUES (${podId}, '', ${nodeId})\n ON CONFLICT (pod_id) DO UPDATE SET node_id = ${nodeId}\n `);\n }\n\n /**\n * Update migration status for a Pod in identity_pod_usage table.\n */\n public async setMigrationStatus(\n podId: string,\n status: 'syncing' | 'done' | null,\n targetNode?: string | null,\n progress?: number | null,\n ): Promise<void> {\n const tableId = sql.identifier([this.usageTableName]);\n await executeStatement(this.db, sql`\n INSERT INTO ${tableId} (pod_id, account_id, migration_status, migration_target_node, migration_progress)\n VALUES (${podId}, '', ${status}, ${targetNode ?? null}, ${progress ?? 0})\n ON CONFLICT (pod_id) DO UPDATE SET\n migration_status = ${status},\n migration_target_node = ${targetNode ?? null},\n migration_progress = ${progress ?? 0}\n `);\n }\n\n /**\n * List all pods.\n */\n public async listAllPods(): Promise<PodLookupResult[]> {\n return this.getAllPods();\n }\n\n /**\n * Extract all pods from CSS's internal_kv storage.\n *\n * It keeps backward compatibility with legacy rows that already expose\n * id/account_id/base_url columns (used by some unit tests and older schemas).\n */\n private async getAllPods(): Promise<PodLookupResult[]> {\n const kvTableId = sql.identifier([this.kvTableName]);\n\n const result = await executeQuery<InternalKvRow>(this.db, sql`\n SELECT key, value FROM ${kvTableId}\n WHERE key LIKE 'accounts/data/%'\n `);\n\n const pods: PodLookupResult[] = [];\n\n for (const row of result.rows) {\n if (row.id && row.account_id && row.base_url) {\n pods.push({\n podId: String(row.id),\n accountId: String(row.account_id),\n baseUrl: String(row.base_url),\n nodeId: row.node_id ? String(row.node_id) : undefined,\n edgeNodeId: row.edge_node_id ? String(row.edge_node_id) : undefined,\n });\n continue;\n }\n\n if (!row.key || row.value === undefined) {\n continue;\n }\n\n try {\n const accountId = row.key.replace('accounts/data/', '');\n const data = typeof row.value === 'string' ? JSON.parse(row.value) : row.value;\n\n const podMap = (data as any)['**pod**'] || (data as any).pod || {};\n const webIds = extractAccountWebIds(data);\n\n for (const [podId, podData] of Object.entries(podMap)) {\n const pod = podData as Record<string, unknown>;\n if (pod.baseUrl && typeof pod.baseUrl === 'string') {\n const podWebIds = [\n typeof pod.webId === 'string' ? pod.webId : undefined,\n ...extractPodOwnerWebIds(pod),\n ...webIds,\n ].filter((value): value is string => typeof value === 'string');\n pods.push({\n podId,\n accountId,\n baseUrl: pod.baseUrl,\n webId: dedupeStrings(podWebIds)[0],\n nodeId: typeof pod.nodeId === 'string' ? pod.nodeId : undefined,\n edgeNodeId: typeof pod.edgeNodeId === 'string' ? pod.edgeNodeId : undefined,\n });\n }\n }\n } catch {\n // Skip malformed entries.\n }\n }\n\n return pods;\n }\n}\n\nfunction extractAccountWebIds(data: unknown): string[] {\n if (!data || typeof data !== 'object') {\n return [];\n }\n\n const record = data as Record<string, unknown>;\n const linkMap = record['**webIdLink**'] || record.webIdLink || {};\n if (!linkMap || typeof linkMap !== 'object') {\n return [];\n }\n\n return Object.values(linkMap as Record<string, unknown>)\n .map((value) => {\n if (!value || typeof value !== 'object') {\n return undefined;\n }\n const webId = (value as Record<string, unknown>).webId;\n return typeof webId === 'string' ? webId : undefined;\n })\n .filter((value): value is string => typeof value === 'string');\n}\n\nfunction extractPodOwnerWebIds(pod: Record<string, unknown>): string[] {\n const ownerMap = pod['**owner**'] || pod.owner || {};\n if (!ownerMap || typeof ownerMap !== 'object') {\n return [];\n }\n\n return Object.values(ownerMap as Record<string, unknown>)\n .map((value) => {\n if (!value || typeof value !== 'object') {\n return undefined;\n }\n const webId = (value as Record<string, unknown>).webId;\n return typeof webId === 'string' ? webId : undefined;\n })\n .filter((value): value is string => typeof value === 'string');\n}\n\nfunction normalizeWebId(webId: string | undefined): string | undefined {\n if (!webId) {\n return undefined;\n }\n try {\n return new URL(webId).toString();\n } catch {\n return webId;\n }\n}\n\nfunction dedupeStrings(values: string[]): string[] {\n return [...new Set(values)];\n}\n"]}
|
|
@@ -30,9 +30,11 @@ export interface WebIdProfileRepositoryOptions {
|
|
|
30
30
|
db?: IdentityDatabase;
|
|
31
31
|
identityDbUrl?: string;
|
|
32
32
|
}
|
|
33
|
+
export type WebIdProfileTurtleInput = Pick<WebIdProfile, 'webidUrl' | 'storageUrl' | 'oidcIssuer'>;
|
|
33
34
|
export declare class WebIdProfileRepository {
|
|
34
35
|
private readonly baseUrl;
|
|
35
36
|
private readonly db;
|
|
37
|
+
private readonly schema;
|
|
36
38
|
constructor(options: WebIdProfileRepositoryOptions);
|
|
37
39
|
/**
|
|
38
40
|
* 创建 WebID Profile
|
|
@@ -53,7 +55,7 @@ export declare class WebIdProfileRepository {
|
|
|
53
55
|
/**
|
|
54
56
|
* 生成 WebID Profile 的 Turtle 格式
|
|
55
57
|
*/
|
|
56
|
-
generateProfileTurtle(profile:
|
|
58
|
+
generateProfileTurtle(profile: WebIdProfileTurtleInput): string;
|
|
57
59
|
/**
|
|
58
60
|
* 生成默认的 Profile 数据(JSON-LD 格式)
|
|
59
61
|
*/
|
|
@@ -13,6 +13,7 @@ const logger = (0, global_logger_factory_1.getLoggerFor)('WebIdProfileRepository
|
|
|
13
13
|
class WebIdProfileRepository {
|
|
14
14
|
constructor(options) {
|
|
15
15
|
this.db = options.db ?? getIdentityDatabaseFromOptions(options);
|
|
16
|
+
this.schema = (0, db_1.getSchema)(this.db);
|
|
16
17
|
if (!options.baseUrl) {
|
|
17
18
|
throw new Error('WebIdProfileRepository requires baseUrl.');
|
|
18
19
|
}
|
|
@@ -32,7 +33,7 @@ class WebIdProfileRepository {
|
|
|
32
33
|
// 生成默认的 Profile 数据
|
|
33
34
|
const profileData = this.generateDefaultProfile(username, webidUrl, defaultStorageUrl);
|
|
34
35
|
const now = new Date();
|
|
35
|
-
await this.db.insert(this.
|
|
36
|
+
await this.db.insert(this.schema.webidProfiles).values({
|
|
36
37
|
username,
|
|
37
38
|
webidUrl,
|
|
38
39
|
storageUrl: defaultStorageUrl,
|
|
@@ -62,8 +63,8 @@ class WebIdProfileRepository {
|
|
|
62
63
|
async get(username) {
|
|
63
64
|
const results = await this.db
|
|
64
65
|
.select()
|
|
65
|
-
.from(this.
|
|
66
|
-
.where((0, drizzle_orm_1.eq)(this.
|
|
66
|
+
.from(this.schema.webidProfiles)
|
|
67
|
+
.where((0, drizzle_orm_1.eq)(this.schema.webidProfiles.username, username))
|
|
67
68
|
.limit(1);
|
|
68
69
|
if (results.length === 0) {
|
|
69
70
|
return null;
|
|
@@ -97,14 +98,14 @@ class WebIdProfileRepository {
|
|
|
97
98
|
};
|
|
98
99
|
const now = new Date();
|
|
99
100
|
await this.db
|
|
100
|
-
.update(this.
|
|
101
|
+
.update(this.schema.webidProfiles)
|
|
101
102
|
.set({
|
|
102
103
|
storageUrl,
|
|
103
104
|
storageMode: storageMode ?? existing.storageMode,
|
|
104
105
|
profileData,
|
|
105
106
|
updatedAt: now,
|
|
106
107
|
})
|
|
107
|
-
.where((0, drizzle_orm_1.eq)(this.
|
|
108
|
+
.where((0, drizzle_orm_1.eq)(this.schema.webidProfiles.username, username));
|
|
108
109
|
logger.info(`Updated storage for ${username}: ${storageUrl}`);
|
|
109
110
|
return {
|
|
110
111
|
...existing,
|
|
@@ -119,8 +120,8 @@ class WebIdProfileRepository {
|
|
|
119
120
|
*/
|
|
120
121
|
async delete(username) {
|
|
121
122
|
const result = await this.db
|
|
122
|
-
.delete(this.
|
|
123
|
-
.where((0, drizzle_orm_1.eq)(this.
|
|
123
|
+
.delete(this.schema.webidProfiles)
|
|
124
|
+
.where((0, drizzle_orm_1.eq)(this.schema.webidProfiles.username, username));
|
|
124
125
|
// drizzle 返回的结果格式因数据库而异
|
|
125
126
|
return true;
|
|
126
127
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WebIdProfileRepository.js","sourceRoot":"","sources":["../../../src/identity/drizzle/WebIdProfileRepository.ts"],"names":[],"mappings":";AAAA;;;;GAIG;;;AAEH,6CAAiC;AACjC,6BAAkE;AAClE,iEAAqD;AAErD,MAAM,MAAM,GAAG,IAAA,oCAAY,EAAC,wBAAwB,CAAC,CAAC;AAgCtD,MAAa,sBAAsB;IAIjC,YAAmB,OAAsC;QACvD,IAAI,CAAC,EAAE,GAAG,OAAO,CAAC,EAAE,IAAI,8BAA8B,CAAC,OAAO,CAAC,CAAC;QAChE,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC;YACrB,MAAM,IAAI,KAAK,CAAC,0CAA0C,CAAC,CAAC;QAC9D,CAAC;QACD,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IACpD,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,MAAM,CAAC,KAA8B;QACzC,MAAM,EAAE,QAAQ,EAAE,WAAW,GAAG,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,GAAG,KAAK,CAAC;QAEzE,eAAe;QACf,MAAM,QAAQ,GAAG,GAAG,IAAI,CAAC,OAAO,IAAI,QAAQ,kBAAkB,CAAC;QAE/D,iBAAiB;QACjB,MAAM,iBAAiB,GAAG,WAAW,KAAK,OAAO;YAC/C,CAAC,CAAC,GAAG,IAAI,CAAC,OAAO,IAAI,QAAQ,GAAG;YAChC,CAAC,CAAC,UAAU,CAAC;QAEf,mBAAmB;QACnB,MAAM,WAAW,GAAG,IAAI,CAAC,sBAAsB,CAAC,QAAQ,EAAE,QAAQ,EAAE,iBAAiB,CAAC,CAAC;QAEvF,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC;QAEvB,MAAM,IAAI,CAAC,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC,MAAM,CAAC;YACxD,QAAQ;YACR,QAAQ;YACR,UAAU,EAAE,iBAAiB;YAC7B,WAAW;YACX,UAAU,EAAE,IAAI,CAAC,OAAO;YACxB,WAAW;YACX,SAAS;YACT,SAAS,EAAE,GAAG;YACd,SAAS,EAAE,GAAG;SACf,CAAC,CAAC;QAEH,MAAM,CAAC,IAAI,CAAC,6BAA6B,QAAQ,KAAK,QAAQ,EAAE,CAAC,CAAC;QAElE,OAAO;YACL,QAAQ;YACR,QAAQ;YACR,UAAU,EAAE,iBAAiB;YAC7B,WAAW;YACX,UAAU,EAAE,IAAI,CAAC,OAAO;YACxB,WAAW;YACX,SAAS;YACT,SAAS,EAAE,GAAG;YACd,SAAS,EAAE,GAAG;SACf,CAAC;IACJ,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,GAAG,CAAC,QAAgB;QACxB,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,EAAE;aAC1B,MAAM,EAAE;aACR,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,MAAM,CAAC,aAAa,CAAC;aAClC,KAAK,CAAC,IAAA,gBAAE,EAAC,IAAI,CAAC,EAAE,CAAC,MAAM,CAAC,aAAa,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;aAC1D,KAAK,CAAC,CAAC,CAAC,CAAC;QAEZ,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACzB,OAAO,IAAI,CAAC;QACd,CAAC;QAED,MAAM,GAAG,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;QACvB,OAAO;YACL,QAAQ,EAAE,GAAG,CAAC,QAAQ;YACtB,QAAQ,EAAE,GAAG,CAAC,QAAQ;YACtB,UAAU,EAAE,GAAG,CAAC,UAAU,IAAI,SAAS;YACvC,WAAW,EAAG,GAAG,CAAC,WAA4C,IAAI,OAAO;YACzE,UAAU,EAAE,GAAG,CAAC,UAAU,IAAI,SAAS;YACvC,WAAW,EAAE,GAAG,CAAC,WAAkD;YACnE,SAAS,EAAE,GAAG,CAAC,SAAS,IAAI,SAAS;YACrC,SAAS,EAAE,GAAG,CAAC,SAAS;YACxB,SAAS,EAAE,GAAG,CAAC,SAAS;SACzB,CAAC;IACJ,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,aAAa,CAAC,QAAgB,EAAE,KAAyB;QAC7D,MAAM,EAAE,UAAU,EAAE,WAAW,EAAE,GAAG,KAAK,CAAC;QAE1C,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QAC1C,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,OAAO,IAAI,CAAC;QACd,CAAC;QAED,0BAA0B;QAC1B,MAAM,WAAW,GAAG;YAClB,GAAG,CAAC,QAAQ,CAAC,WAAW,IAAI,EAAE,CAAC;YAC/B,eAAe,EAAE,EAAE,KAAK,EAAE,UAAU,EAAE;SACvC,CAAC;QAEF,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC;QAEvB,MAAM,IAAI,CAAC,EAAE;aACV,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,MAAM,CAAC,aAAa,CAAC;aACpC,GAAG,CAAC;YACH,UAAU;YACV,WAAW,EAAE,WAAW,IAAI,QAAQ,CAAC,WAAW;YAChD,WAAW;YACX,SAAS,EAAE,GAAG;SACf,CAAC;aACD,KAAK,CAAC,IAAA,gBAAE,EAAC,IAAI,CAAC,EAAE,CAAC,MAAM,CAAC,aAAa,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC;QAE9D,MAAM,CAAC,IAAI,CAAC,uBAAuB,QAAQ,KAAK,UAAU,EAAE,CAAC,CAAC;QAE9D,OAAO;YACL,GAAG,QAAQ;YACX,UAAU;YACV,WAAW,EAAE,WAAW,IAAI,QAAQ,CAAC,WAAW;YAChD,WAAW;YACX,SAAS,EAAE,GAAG;SACf,CAAC;IACJ,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,MAAM,CAAC,QAAgB;QAC3B,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,EAAE;aACzB,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,MAAM,CAAC,aAAa,CAAC;aACpC,KAAK,CAAC,IAAA,gBAAE,EAAC,IAAI,CAAC,EAAE,CAAC,MAAM,CAAC,aAAa,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC;QAE9D,wBAAwB;QACxB,OAAO,IAAI,CAAC;IACd,CAAC;IAED;;OAEG;IACH,qBAAqB,CAAC,OAAqB;QACzC,MAAM,EAAE,QAAQ,EAAE,UAAU,EAAE,UAAU,EAAE,GAAG,OAAO,CAAC;QAErD,OAAO;;;GAGR,QAAQ;;wBAEa,UAAU,IAAI,UAAU,CAAC,CAAC,CAAC;qBAC9B,UAAU,GAAG,CAAC,CAAC,CAAC,EAAE;CACtC,CAAC;IACA,CAAC;IAED;;OAEG;IACK,sBAAsB,CAC5B,QAAgB,EAChB,QAAgB,EAChB,UAAmB;QAEnB,MAAM,OAAO,GAA4B;YACvC,UAAU,EAAE;gBACV,IAAI,EAAE,4BAA4B;gBAClC,KAAK,EAAE,mCAAmC;aAC3C;YACD,KAAK,EAAE,QAAQ;YACf,OAAO,EAAE,aAAa;YACtB,kBAAkB,EAAE,EAAE,KAAK,EAAE,IAAI,CAAC,OAAO,EAAE;SAC5C,CAAC;QAEF,IAAI,UAAU,EAAE,CAAC;YACf,OAAO,CAAC,eAAe,CAAC,GAAG,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC;QACnD,CAAC;QAED,OAAO,OAAO,CAAC;IACjB,CAAC;CACF;AAlLD,wDAkLC;AAED,SAAS,8BAA8B,CAAC,OAAsC;IAC5E,IAAI,OAAO,CAAC,aAAa,EAAE,CAAC;QAC1B,OAAO,IAAA,wBAAmB,EAAC,OAAO,CAAC,aAAa,CAAC,CAAC;IACpD,CAAC;IAED,MAAM,IAAI,KAAK,CAAC,sDAAsD,CAAC,CAAC;AAC1E,CAAC","sourcesContent":["/**\n * WebID Profile Repository\n *\n * 管理 WebID Profile 的托管,支持身份与存储分离架构\n */\n\nimport { eq } from 'drizzle-orm';\nimport { getIdentityDatabase, type IdentityDatabase } from './db';\nimport { getLoggerFor } from 'global-logger-factory';\n\nconst logger = getLoggerFor('WebIdProfileRepository');\n\nexport interface WebIdProfile {\n username: string;\n webidUrl: string;\n storageUrl?: string;\n storageMode: 'cloud' | 'local' | 'custom';\n oidcIssuer?: string;\n profileData?: Record<string, unknown>;\n accountId?: string;\n createdAt: Date;\n updatedAt: Date;\n}\n\nexport interface CreateWebIdProfileInput {\n username: string;\n storageMode?: 'cloud' | 'local' | 'custom';\n storageUrl?: string;\n accountId?: string;\n}\n\nexport interface UpdateStorageInput {\n storageUrl: string;\n storageMode?: 'cloud' | 'local' | 'custom';\n}\n\nexport interface WebIdProfileRepositoryOptions {\n baseUrl: string;\n db?: IdentityDatabase;\n identityDbUrl?: string;\n}\n\nexport class WebIdProfileRepository {\n private readonly baseUrl: string;\n private readonly db: IdentityDatabase;\n\n public constructor(options: WebIdProfileRepositoryOptions) {\n this.db = options.db ?? getIdentityDatabaseFromOptions(options);\n if (!options.baseUrl) {\n throw new Error('WebIdProfileRepository requires baseUrl.');\n }\n this.baseUrl = options.baseUrl.replace(/\\/$/, '');\n }\n\n /**\n * 创建 WebID Profile\n */\n async create(input: CreateWebIdProfileInput): Promise<WebIdProfile> {\n const { username, storageMode = 'cloud', storageUrl, accountId } = input;\n\n // 生成 WebID URL\n const webidUrl = `${this.baseUrl}/${username}/profile/card#me`;\n\n // 默认 storage URL\n const defaultStorageUrl = storageMode === 'cloud'\n ? `${this.baseUrl}/${username}/`\n : storageUrl;\n\n // 生成默认的 Profile 数据\n const profileData = this.generateDefaultProfile(username, webidUrl, defaultStorageUrl);\n\n const now = new Date();\n\n await this.db.insert(this.db.schema.webidProfiles).values({\n username,\n webidUrl,\n storageUrl: defaultStorageUrl,\n storageMode,\n oidcIssuer: this.baseUrl,\n profileData,\n accountId,\n createdAt: now,\n updatedAt: now,\n });\n\n logger.info(`Created WebID profile for ${username}: ${webidUrl}`);\n\n return {\n username,\n webidUrl,\n storageUrl: defaultStorageUrl,\n storageMode,\n oidcIssuer: this.baseUrl,\n profileData,\n accountId,\n createdAt: now,\n updatedAt: now,\n };\n }\n\n /**\n * 获取 WebID Profile\n */\n async get(username: string): Promise<WebIdProfile | null> {\n const results = await this.db\n .select()\n .from(this.db.schema.webidProfiles)\n .where(eq(this.db.schema.webidProfiles.username, username))\n .limit(1);\n\n if (results.length === 0) {\n return null;\n }\n\n const row = results[0];\n return {\n username: row.username,\n webidUrl: row.webidUrl,\n storageUrl: row.storageUrl ?? undefined,\n storageMode: (row.storageMode as 'cloud' | 'local' | 'custom') ?? 'cloud',\n oidcIssuer: row.oidcIssuer ?? undefined,\n profileData: row.profileData as Record<string, unknown> | undefined,\n accountId: row.accountId ?? undefined,\n createdAt: row.createdAt,\n updatedAt: row.updatedAt,\n };\n }\n\n /**\n * 更新 Storage URL(用于 Local 节点更新指针)\n */\n async updateStorage(username: string, input: UpdateStorageInput): Promise<WebIdProfile | null> {\n const { storageUrl, storageMode } = input;\n\n const existing = await this.get(username);\n if (!existing) {\n return null;\n }\n\n // 更新 Profile 数据中的 storage\n const profileData = {\n ...(existing.profileData ?? {}),\n 'solid:storage': { '@id': storageUrl },\n };\n\n const now = new Date();\n\n await this.db\n .update(this.db.schema.webidProfiles)\n .set({\n storageUrl,\n storageMode: storageMode ?? existing.storageMode,\n profileData,\n updatedAt: now,\n })\n .where(eq(this.db.schema.webidProfiles.username, username));\n\n logger.info(`Updated storage for ${username}: ${storageUrl}`);\n\n return {\n ...existing,\n storageUrl,\n storageMode: storageMode ?? existing.storageMode,\n profileData,\n updatedAt: now,\n };\n }\n\n /**\n * 删除 WebID Profile\n */\n async delete(username: string): Promise<boolean> {\n const result = await this.db\n .delete(this.db.schema.webidProfiles)\n .where(eq(this.db.schema.webidProfiles.username, username));\n\n // drizzle 返回的结果格式因数据库而异\n return true;\n }\n\n /**\n * 生成 WebID Profile 的 Turtle 格式\n */\n generateProfileTurtle(profile: WebIdProfile): string {\n const { webidUrl, storageUrl, oidcIssuer } = profile;\n\n return `@prefix foaf: <http://xmlns.com/foaf/0.1/>.\n@prefix solid: <http://www.w3.org/ns/solid/terms#>.\n\n<${webidUrl}>\n a foaf:Person;\n solid:oidcIssuer <${oidcIssuer}>${storageUrl ? `;\n solid:storage <${storageUrl}>` : ''}.\n`;\n }\n\n /**\n * 生成默认的 Profile 数据(JSON-LD 格式)\n */\n private generateDefaultProfile(\n username: string,\n webidUrl: string,\n storageUrl?: string,\n ): Record<string, unknown> {\n const profile: Record<string, unknown> = {\n '@context': {\n foaf: 'http://xmlns.com/foaf/0.1/',\n solid: 'http://www.w3.org/ns/solid/terms#',\n },\n '@id': webidUrl,\n '@type': 'foaf:Person',\n 'solid:oidcIssuer': { '@id': this.baseUrl },\n };\n\n if (storageUrl) {\n profile['solid:storage'] = { '@id': storageUrl };\n }\n\n return profile;\n }\n}\n\nfunction getIdentityDatabaseFromOptions(options: WebIdProfileRepositoryOptions): IdentityDatabase {\n if (options.identityDbUrl) {\n return getIdentityDatabase(options.identityDbUrl);\n }\n\n throw new Error('WebIdProfileRepository requires db or identityDbUrl.');\n}\n"]}
|
|
1
|
+
{"version":3,"file":"WebIdProfileRepository.js","sourceRoot":"","sources":["../../../src/identity/drizzle/WebIdProfileRepository.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * WebID Profile Repository\n *\n * 管理 WebID Profile 的托管,支持身份与存储分离架构\n */\n\nimport { eq } from 'drizzle-orm';\nimport { getIdentityDatabase, getSchema, type IdentityDatabase, type IdentitySchema } from './db';\nimport { getLoggerFor } from 'global-logger-factory';\n\nconst logger = getLoggerFor('WebIdProfileRepository');\n\nexport interface WebIdProfile {\n username: string;\n webidUrl: string;\n storageUrl?: string;\n storageMode: 'cloud' | 'local' | 'custom';\n oidcIssuer?: string;\n profileData?: Record<string, unknown>;\n accountId?: string;\n createdAt: Date;\n updatedAt: Date;\n}\n\nexport interface CreateWebIdProfileInput {\n username: string;\n storageMode?: 'cloud' | 'local' | 'custom';\n storageUrl?: string;\n accountId?: string;\n}\n\nexport interface UpdateStorageInput {\n storageUrl: string;\n storageMode?: 'cloud' | 'local' | 'custom';\n}\n\nexport interface WebIdProfileRepositoryOptions {\n baseUrl: string;\n db?: IdentityDatabase;\n identityDbUrl?: string;\n}\n\nexport type WebIdProfileTurtleInput = Pick<WebIdProfile, 'webidUrl' | 'storageUrl' | 'oidcIssuer'>;\n\nexport class WebIdProfileRepository {\n private readonly baseUrl: string;\n private readonly db: IdentityDatabase;\n private readonly schema: IdentitySchema;\n\n public constructor(options: WebIdProfileRepositoryOptions) {\n this.db = options.db ?? getIdentityDatabaseFromOptions(options);\n this.schema = getSchema(this.db);\n if (!options.baseUrl) {\n throw new Error('WebIdProfileRepository requires baseUrl.');\n }\n this.baseUrl = options.baseUrl.replace(/\\/$/, '');\n }\n\n /**\n * 创建 WebID Profile\n */\n async create(input: CreateWebIdProfileInput): Promise<WebIdProfile> {\n const { username, storageMode = 'cloud', storageUrl, accountId } = input;\n\n // 生成 WebID URL\n const webidUrl = `${this.baseUrl}/${username}/profile/card#me`;\n\n // 默认 storage URL\n const defaultStorageUrl = storageMode === 'cloud'\n ? `${this.baseUrl}/${username}/`\n : storageUrl;\n\n // 生成默认的 Profile 数据\n const profileData = this.generateDefaultProfile(username, webidUrl, defaultStorageUrl);\n\n const now = new Date();\n\n await this.db.insert(this.schema.webidProfiles).values({\n username,\n webidUrl,\n storageUrl: defaultStorageUrl,\n storageMode,\n oidcIssuer: this.baseUrl,\n profileData,\n accountId,\n createdAt: now,\n updatedAt: now,\n });\n\n logger.info(`Created WebID profile for ${username}: ${webidUrl}`);\n\n return {\n username,\n webidUrl,\n storageUrl: defaultStorageUrl,\n storageMode,\n oidcIssuer: this.baseUrl,\n profileData,\n accountId,\n createdAt: now,\n updatedAt: now,\n };\n }\n\n /**\n * 获取 WebID Profile\n */\n async get(username: string): Promise<WebIdProfile | null> {\n const results = await this.db\n .select()\n .from(this.schema.webidProfiles)\n .where(eq(this.schema.webidProfiles.username, username))\n .limit(1);\n\n if (results.length === 0) {\n return null;\n }\n\n const row = results[0];\n return {\n username: row.username,\n webidUrl: row.webidUrl,\n storageUrl: row.storageUrl ?? undefined,\n storageMode: (row.storageMode as 'cloud' | 'local' | 'custom') ?? 'cloud',\n oidcIssuer: row.oidcIssuer ?? undefined,\n profileData: row.profileData as Record<string, unknown> | undefined,\n accountId: row.accountId ?? undefined,\n createdAt: row.createdAt,\n updatedAt: row.updatedAt,\n };\n }\n\n /**\n * 更新 Storage URL(用于 Local 节点更新指针)\n */\n async updateStorage(username: string, input: UpdateStorageInput): Promise<WebIdProfile | null> {\n const { storageUrl, storageMode } = input;\n\n const existing = await this.get(username);\n if (!existing) {\n return null;\n }\n\n // 更新 Profile 数据中的 storage\n const profileData = {\n ...(existing.profileData ?? {}),\n 'solid:storage': { '@id': storageUrl },\n };\n\n const now = new Date();\n\n await this.db\n .update(this.schema.webidProfiles)\n .set({\n storageUrl,\n storageMode: storageMode ?? existing.storageMode,\n profileData,\n updatedAt: now,\n })\n .where(eq(this.schema.webidProfiles.username, username));\n\n logger.info(`Updated storage for ${username}: ${storageUrl}`);\n\n return {\n ...existing,\n storageUrl,\n storageMode: storageMode ?? existing.storageMode,\n profileData,\n updatedAt: now,\n };\n }\n\n /**\n * 删除 WebID Profile\n */\n async delete(username: string): Promise<boolean> {\n const result = await this.db\n .delete(this.schema.webidProfiles)\n .where(eq(this.schema.webidProfiles.username, username));\n\n // drizzle 返回的结果格式因数据库而异\n return true;\n }\n\n /**\n * 生成 WebID Profile 的 Turtle 格式\n */\n generateProfileTurtle(profile: WebIdProfileTurtleInput): string {\n const { webidUrl, storageUrl, oidcIssuer } = profile;\n\n return `@prefix foaf: <http://xmlns.com/foaf/0.1/>.\n@prefix solid: <http://www.w3.org/ns/solid/terms#>.\n\n<${webidUrl}>\n a foaf:Person;\n solid:oidcIssuer <${oidcIssuer}>${storageUrl ? `;\n solid:storage <${storageUrl}>` : ''}.\n`;\n }\n\n /**\n * 生成默认的 Profile 数据(JSON-LD 格式)\n */\n private generateDefaultProfile(\n username: string,\n webidUrl: string,\n storageUrl?: string,\n ): Record<string, unknown> {\n const profile: Record<string, unknown> = {\n '@context': {\n foaf: 'http://xmlns.com/foaf/0.1/',\n solid: 'http://www.w3.org/ns/solid/terms#',\n },\n '@id': webidUrl,\n '@type': 'foaf:Person',\n 'solid:oidcIssuer': { '@id': this.baseUrl },\n };\n\n if (storageUrl) {\n profile['solid:storage'] = { '@id': storageUrl };\n }\n\n return profile;\n }\n}\n\nfunction getIdentityDatabaseFromOptions(options: WebIdProfileRepositoryOptions): IdentityDatabase {\n if (options.identityDbUrl) {\n return getIdentityDatabase(options.identityDbUrl);\n }\n\n throw new Error('WebIdProfileRepository requires db or identityDbUrl.');\n}\n"]}
|
|
@@ -49,6 +49,10 @@
|
|
|
49
49
|
"@id": "undefineds:dist/identity/drizzle/WebIdProfileRepository.jsonld#WebIdProfileRepository__member_db",
|
|
50
50
|
"memberFieldName": "db"
|
|
51
51
|
},
|
|
52
|
+
{
|
|
53
|
+
"@id": "undefineds:dist/identity/drizzle/WebIdProfileRepository.jsonld#WebIdProfileRepository__member_schema",
|
|
54
|
+
"memberFieldName": "schema"
|
|
55
|
+
},
|
|
52
56
|
{
|
|
53
57
|
"@id": "undefineds:dist/identity/drizzle/WebIdProfileRepository.jsonld#WebIdProfileRepository__member_constructor",
|
|
54
58
|
"memberFieldName": "constructor"
|
|
@@ -256,6 +256,18 @@ function ensureSqliteTables(sqlite) {
|
|
|
256
256
|
updated_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now'))
|
|
257
257
|
);
|
|
258
258
|
|
|
259
|
+
CREATE TABLE IF NOT EXISTS identity_webid_profile (
|
|
260
|
+
username TEXT PRIMARY KEY,
|
|
261
|
+
webid_url TEXT NOT NULL,
|
|
262
|
+
storage_url TEXT,
|
|
263
|
+
storage_mode TEXT DEFAULT 'cloud',
|
|
264
|
+
oidc_issuer TEXT,
|
|
265
|
+
profile_data TEXT,
|
|
266
|
+
account_id TEXT,
|
|
267
|
+
created_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now')),
|
|
268
|
+
updated_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now'))
|
|
269
|
+
);
|
|
270
|
+
|
|
259
271
|
CREATE TABLE IF NOT EXISTS identity_edge_node (
|
|
260
272
|
id TEXT PRIMARY KEY,
|
|
261
273
|
display_name TEXT,
|
|
@@ -424,6 +436,25 @@ async function migratePgColumns(pool) {
|
|
|
424
436
|
await addColumn('identity_pod_usage', 'compute_limit_seconds', 'BIGINT');
|
|
425
437
|
await addColumn('identity_pod_usage', 'token_limit_monthly', 'BIGINT');
|
|
426
438
|
await addColumn('identity_pod_usage', 'period_start', 'TIMESTAMP WITH TIME ZONE');
|
|
439
|
+
// WebID profile table
|
|
440
|
+
try {
|
|
441
|
+
await pool.query(`
|
|
442
|
+
CREATE TABLE IF NOT EXISTS identity_webid_profile (
|
|
443
|
+
username TEXT PRIMARY KEY,
|
|
444
|
+
webid_url TEXT NOT NULL,
|
|
445
|
+
storage_url TEXT,
|
|
446
|
+
storage_mode TEXT DEFAULT 'cloud',
|
|
447
|
+
oidc_issuer TEXT,
|
|
448
|
+
profile_data TEXT,
|
|
449
|
+
account_id TEXT,
|
|
450
|
+
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
|
|
451
|
+
updated_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW()
|
|
452
|
+
);
|
|
453
|
+
`);
|
|
454
|
+
}
|
|
455
|
+
catch {
|
|
456
|
+
// Ignore if identity profile storage is unavailable.
|
|
457
|
+
}
|
|
427
458
|
// Service token table
|
|
428
459
|
try {
|
|
429
460
|
await pool.query(`
|
|
@@ -465,6 +496,18 @@ async function ensurePostgresTables(pool) {
|
|
|
465
496
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
466
497
|
);
|
|
467
498
|
|
|
499
|
+
CREATE TABLE IF NOT EXISTS identity_webid_profile (
|
|
500
|
+
username TEXT PRIMARY KEY,
|
|
501
|
+
webid_url TEXT NOT NULL,
|
|
502
|
+
storage_url TEXT,
|
|
503
|
+
storage_mode TEXT DEFAULT 'cloud',
|
|
504
|
+
oidc_issuer TEXT,
|
|
505
|
+
profile_data TEXT,
|
|
506
|
+
account_id TEXT,
|
|
507
|
+
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
508
|
+
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
509
|
+
);
|
|
510
|
+
|
|
468
511
|
CREATE TABLE IF NOT EXISTS identity_edge_node (
|
|
469
512
|
id TEXT PRIMARY KEY,
|
|
470
513
|
display_name TEXT,
|
|
@@ -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' | '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_ddns_domain (\n domain TEXT PRIMARY KEY,\n status TEXT DEFAULT 'active',\n provider TEXT,\n zone_id TEXT,\n created_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now'))\n );\n\n CREATE TABLE IF NOT EXISTS identity_ddns_record (\n subdomain TEXT PRIMARY KEY,\n domain TEXT NOT NULL,\n ip_address TEXT,\n ipv6_address TEXT,\n record_type TEXT DEFAULT 'A',\n node_id TEXT,\n username TEXT,\n status TEXT DEFAULT 'active',\n banned_reason TEXT,\n ttl INTEGER DEFAULT 60,\n created_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now')),\n updated_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 CREATE TABLE IF NOT EXISTS identity_ddns_domain (\n domain TEXT PRIMARY KEY,\n status TEXT DEFAULT 'active',\n provider TEXT,\n zone_id TEXT,\n created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()\n );\n\n CREATE TABLE IF NOT EXISTS identity_ddns_record (\n subdomain TEXT PRIMARY KEY,\n domain TEXT NOT NULL,\n ip_address TEXT,\n ipv6_address TEXT,\n record_type TEXT DEFAULT 'A',\n node_id TEXT,\n username TEXT,\n status TEXT DEFAULT 'active',\n banned_reason TEXT,\n ttl INTEGER DEFAULT 60,\n created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),\n updated_at TIMESTAMPTZ NOT NULL DEFAULT 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 TIMESTAMPTZ NOT NULL DEFAULT NOW(),\n expires_at TIMESTAMPTZ\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"]}
|
|
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_webid_profile (\n username TEXT PRIMARY KEY,\n webid_url TEXT NOT NULL,\n storage_url TEXT,\n storage_mode TEXT DEFAULT 'cloud',\n oidc_issuer TEXT,\n profile_data TEXT,\n account_id TEXT,\n created_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now')),\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_ddns_domain (\n domain TEXT PRIMARY KEY,\n status TEXT DEFAULT 'active',\n provider TEXT,\n zone_id TEXT,\n created_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now'))\n );\n\n CREATE TABLE IF NOT EXISTS identity_ddns_record (\n subdomain TEXT PRIMARY KEY,\n domain TEXT NOT NULL,\n ip_address TEXT,\n ipv6_address TEXT,\n record_type TEXT DEFAULT 'A',\n node_id TEXT,\n username TEXT,\n status TEXT DEFAULT 'active',\n banned_reason TEXT,\n ttl INTEGER DEFAULT 60,\n created_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now')),\n updated_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 // WebID profile table\n try {\n await pool.query(`\n CREATE TABLE IF NOT EXISTS identity_webid_profile (\n username TEXT PRIMARY KEY,\n webid_url TEXT NOT NULL,\n storage_url TEXT,\n storage_mode TEXT DEFAULT 'cloud',\n oidc_issuer TEXT,\n profile_data TEXT,\n account_id TEXT,\n created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),\n updated_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW()\n );\n `);\n } catch {\n // Ignore if identity profile storage is unavailable.\n }\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_webid_profile (\n username TEXT PRIMARY KEY,\n webid_url TEXT NOT NULL,\n storage_url TEXT,\n storage_mode TEXT DEFAULT 'cloud',\n oidc_issuer TEXT,\n profile_data TEXT,\n account_id TEXT,\n created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),\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 CREATE TABLE IF NOT EXISTS identity_ddns_domain (\n domain TEXT PRIMARY KEY,\n status TEXT DEFAULT 'active',\n provider TEXT,\n zone_id TEXT,\n created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()\n );\n\n CREATE TABLE IF NOT EXISTS identity_ddns_record (\n subdomain TEXT PRIMARY KEY,\n domain TEXT NOT NULL,\n ip_address TEXT,\n ipv6_address TEXT,\n record_type TEXT DEFAULT 'A',\n node_id TEXT,\n username TEXT,\n status TEXT DEFAULT 'active',\n banned_reason TEXT,\n ttl INTEGER DEFAULT 60,\n created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),\n updated_at TIMESTAMPTZ NOT NULL DEFAULT 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 TIMESTAMPTZ NOT NULL DEFAULT NOW(),\n expires_at TIMESTAMPTZ\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"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@undefineds.co/xpod",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.39",
|
|
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",
|