@undefineds.co/xpod 0.2.13 → 0.2.15
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 +8 -4
- package/dist/api/container/local.js.map +1 -1
- package/dist/api/container/routes.js +11 -4
- package/dist/api/container/routes.js.map +1 -1
- package/dist/api/handlers/DdnsHandler.js +35 -49
- package/dist/api/handlers/DdnsHandler.js.map +1 -1
- package/dist/api/handlers/LinxCapabilitiesHandler.d.ts +2 -0
- package/dist/api/handlers/LinxCapabilitiesHandler.js +38 -0
- package/dist/api/handlers/LinxCapabilitiesHandler.js.map +1 -0
- package/dist/api/handlers/ProvisionHandler.d.ts +4 -0
- package/dist/api/handlers/ProvisionHandler.js +104 -15
- package/dist/api/handlers/ProvisionHandler.js.map +1 -1
- package/dist/edge/DdnsManager.d.ts +2 -0
- package/dist/edge/DdnsManager.js +3 -0
- package/dist/edge/DdnsManager.js.map +1 -1
- package/dist/identity/drizzle/EdgeNodeRepository.d.ts +2 -0
- package/dist/identity/drizzle/EdgeNodeRepository.js +1 -1
- package/dist/identity/drizzle/EdgeNodeRepository.js.map +1 -1
- package/dist/subdomain/SubdomainClient.d.ts +9 -0
- package/dist/subdomain/SubdomainClient.js +2 -0
- package/dist/subdomain/SubdomainClient.js.map +1 -1
- package/package.json +1 -1
|
@@ -130,8 +130,14 @@ function registerLocalServices(container) {
|
|
|
130
130
|
// 托管式:有 Node Token,连接 Cloud
|
|
131
131
|
// Cloud API endpoint 可以从 Token 解析或使用默认值
|
|
132
132
|
const effectiveCloudApiEndpoint = cloudApiEndpoint || 'https://pods.undefineds.co';
|
|
133
|
+
const effectiveLocalPort = parseInt(process.env.XPOD_MAIN_PORT || process.env.CSS_PORT || '3000', 10);
|
|
133
134
|
// 从 Node Token 解析用户名作为子域名 (格式: username:secret)
|
|
134
135
|
const subdomain = parseSubdomainFromToken(nodeToken);
|
|
136
|
+
const tunnelProviderHint = cloudflareTunnelToken
|
|
137
|
+
? 'cloudflare'
|
|
138
|
+
: sakuraTunnelToken
|
|
139
|
+
? 'sakura_frp'
|
|
140
|
+
: 'none';
|
|
135
141
|
container.register({
|
|
136
142
|
subdomainClient: (0, awilix_1.asFunction)(() => {
|
|
137
143
|
return new SubdomainClient_1.SubdomainClient({
|
|
@@ -148,15 +154,13 @@ function registerLocalServices(container) {
|
|
|
148
154
|
}).singleton(),
|
|
149
155
|
// DDNS Manager: 自动分配和更新 DDNS
|
|
150
156
|
ddnsManager: (0, awilix_1.asFunction)(({ subdomainClient, capabilityDetector }) => {
|
|
151
|
-
const tunnelProvider = cloudflareTunnelToken
|
|
152
|
-
? 'cloudflare'
|
|
153
|
-
: (sakuraTunnelToken ? 'sakura_frp' : 'none');
|
|
154
157
|
return new DdnsManager_1.DdnsManager({
|
|
155
158
|
client: subdomainClient,
|
|
156
159
|
detector: capabilityDetector,
|
|
157
160
|
subdomain: subdomain || nodeId || 'auto',
|
|
161
|
+
localPort: effectiveLocalPort,
|
|
158
162
|
autoAllocate: true,
|
|
159
|
-
tunnelProvider,
|
|
163
|
+
tunnelProvider: tunnelProviderHint,
|
|
160
164
|
});
|
|
161
165
|
}).singleton(),
|
|
162
166
|
});
|
|
@@ -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, localTunnelProvider }: ApiContainerCradle) => {\n // Tunnel 应该指向 Gateway 端口 (通常是 3000),而不是 API Server 端口 (3004)\n const mainPort = parseInt(process.env.XPOD_MAIN_PORT || '3000', 10);\n return new LocalNetworkManager({\n detector: capabilityDetector!,\n dnsCoordinator: dnsCoordinator!,\n tunnelProvider: localTunnelProvider,\n localPort: mainPort,\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\n // 从 Node Token 解析用户名作为子域名 (格式: username:secret)\n const subdomain = parseSubdomainFromToken(nodeToken);\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 const tunnelProvider = cloudflareTunnelToken\n ? 'cloudflare'\n : (sakuraTunnelToken ? 'sakura_frp' : 'none');\n return new DdnsManager({\n client: subdomainClient!,\n detector: capabilityDetector!,\n subdomain: subdomain || nodeId || 'auto',\n autoAllocate: true,\n tunnelProvider,\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';\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, localTunnelProvider }: ApiContainerCradle) => {\n // Tunnel 应该指向 Gateway 端口 (通常是 3000),而不是 API Server 端口 (3004)\n const mainPort = parseInt(process.env.XPOD_MAIN_PORT || '3000', 10);\n return new LocalNetworkManager({\n detector: capabilityDetector!,\n dnsCoordinator: dnsCoordinator!,\n tunnelProvider: localTunnelProvider,\n localPort: mainPort,\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"]}
|
|
@@ -39,10 +39,10 @@ const WebIdProfileHandler_1 = require("../handlers/WebIdProfileHandler");
|
|
|
39
39
|
const DdnsHandler_1 = require("../handlers/DdnsHandler");
|
|
40
40
|
const ChatKitHandler_1 = require("../handlers/ChatKitHandler");
|
|
41
41
|
const ChatKitV1Handler_1 = require("../handlers/ChatKitV1Handler");
|
|
42
|
-
const VectorHandler_1 = require("../handlers/VectorHandler");
|
|
43
42
|
const DashboardHandler_1 = require("../handlers/DashboardHandler");
|
|
44
43
|
const AdminHandler_1 = require("../handlers/AdminHandler");
|
|
45
44
|
const AdminDdnsHandler_1 = require("../handlers/AdminDdnsHandler");
|
|
45
|
+
const LinxCapabilitiesHandler_1 = require("../handlers/LinxCapabilitiesHandler");
|
|
46
46
|
const ProvisionHandler_1 = require("../handlers/ProvisionHandler");
|
|
47
47
|
const PodManagementHandler_1 = require("../handlers/PodManagementHandler");
|
|
48
48
|
const QuotaHandler_1 = require("../handlers/QuotaHandler");
|
|
@@ -96,7 +96,6 @@ function registerSharedRoutes(container, server) {
|
|
|
96
96
|
const chatService = container.resolve('chatService');
|
|
97
97
|
const chatKitService = container.resolve('chatKitService');
|
|
98
98
|
const chatKitStore = container.resolve('chatKitStore');
|
|
99
|
-
const vectorService = container.resolve('vectorService');
|
|
100
99
|
const config = container.resolve('config');
|
|
101
100
|
(0, EdgeNodeSignalHandler_1.registerEdgeNodeSignalRoutes)(server, {
|
|
102
101
|
repository: nodeRepo,
|
|
@@ -108,7 +107,6 @@ function registerSharedRoutes(container, server) {
|
|
|
108
107
|
(0, ChatHandler_1.registerChatRoutes)(server, { chatService });
|
|
109
108
|
(0, ChatKitHandler_1.registerChatKitRoutes)(server, { chatKitService });
|
|
110
109
|
(0, ChatKitV1Handler_1.registerChatKitV1Routes)(server, { store: chatKitStore });
|
|
111
|
-
(0, VectorHandler_1.registerVectorRoutes)(server, { vectorService });
|
|
112
110
|
// Quota & Usage API (Business 对接)
|
|
113
111
|
try {
|
|
114
112
|
const quotaService = new DrizzleQuotaService_1.DrizzleQuotaService({ identityDbUrl: config.databaseUrl });
|
|
@@ -176,7 +174,15 @@ function registerCloudRoutes(container, server) {
|
|
|
176
174
|
const config = container.resolve('config');
|
|
177
175
|
const baseUrl = process.env.CSS_BASE_URL || 'http://localhost:3000/';
|
|
178
176
|
const baseStorageDomain = config.subdomain?.baseStorageDomain;
|
|
179
|
-
|
|
177
|
+
const ddnsRepo = container.resolve('ddnsRepo', { allowUnregistered: true });
|
|
178
|
+
const tunnelProvider = container.resolve('tunnelProvider', { allowUnregistered: true });
|
|
179
|
+
(0, ProvisionHandler_1.registerProvisionRoutes)(server, {
|
|
180
|
+
repository: nodeRepo,
|
|
181
|
+
ddnsRepo,
|
|
182
|
+
tunnelProvider,
|
|
183
|
+
baseUrl,
|
|
184
|
+
baseStorageDomain,
|
|
185
|
+
});
|
|
180
186
|
console.log(`[Cloud] Provision routes registered${baseStorageDomain ? ` (baseStorageDomain: ${baseStorageDomain})` : ''}`);
|
|
181
187
|
}
|
|
182
188
|
catch {
|
|
@@ -187,6 +193,7 @@ function registerCloudRoutes(container, server) {
|
|
|
187
193
|
* Local 模式专属路由
|
|
188
194
|
*/
|
|
189
195
|
function registerLocalRoutes(container, server) {
|
|
196
|
+
(0, LinxCapabilitiesHandler_1.registerLinxCapabilitiesRoutes)(server);
|
|
190
197
|
// Admin API (配置管理、重启)
|
|
191
198
|
(0, AdminHandler_1.registerAdminRoutes)(server);
|
|
192
199
|
// DDNS status (托管式 Local 模式)
|
|
@@ -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 { registerVectorRoutes } from '../handlers/VectorHandler';\nimport { registerDashboardRoutes } from '../handlers/DashboardHandler';\nimport { registerAdminRoutes } from '../handlers/AdminHandler';\nimport { registerAdminDdnsRoutes } from '../handlers/AdminDdnsHandler';\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 * 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 vectorService = container.resolve('vectorService');\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 registerVectorRoutes(server, { vectorService });\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 if (profileRepo) {\n registerWebIdProfileRoutes(server, { profileRepo: profileRepo as any });\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 registerProvisionRoutes(server, { repository: nodeRepo, baseUrl, baseStorageDomain });\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 // 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 registerPodManagementRoutes(server, {\n rootDir,\n verifyServiceToken: async (token: string) => token === expectedServiceToken,\n });\n console.log('[Local] Pod provision routes registered (/provision/pods)');\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 * 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 if (profileRepo) {\n registerWebIdProfileRoutes(server, { profileRepo: profileRepo as any });\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 tunnelProvider = container.resolve('tunnelProvider', { allowUnregistered: true }) as any;\n registerProvisionRoutes(server, {\n repository: nodeRepo,\n ddnsRepo,\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 registerPodManagementRoutes(server, {\n rootDir,\n verifyServiceToken: async (token: string) => token === expectedServiceToken,\n });\n console.log('[Local] Pod provision routes registered (/provision/pods)');\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"]}
|
|
@@ -13,21 +13,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
13
13
|
exports.registerDdnsRoutes = registerDdnsRoutes;
|
|
14
14
|
const global_logger_factory_1 = require("global-logger-factory");
|
|
15
15
|
const logger = (0, global_logger_factory_1.getLoggerFor)('DdnsHandler');
|
|
16
|
-
function pickRecordType(options) {
|
|
17
|
-
if (options.mode === 'tunnel') {
|
|
18
|
-
return 'CNAME';
|
|
19
|
-
}
|
|
20
|
-
return options.ipv6Address ? 'AAAA' : 'A';
|
|
21
|
-
}
|
|
22
|
-
function pickRecordValue(options) {
|
|
23
|
-
if (options.mode === 'tunnel') {
|
|
24
|
-
if (options.tunnelProvider === 'cloudflare') {
|
|
25
|
-
return `${options.subdomain}.cfargotunnel.com`;
|
|
26
|
-
}
|
|
27
|
-
return undefined;
|
|
28
|
-
}
|
|
29
|
-
return options.ipv6Address ?? options.ipAddress;
|
|
30
|
-
}
|
|
31
16
|
function registerDdnsRoutes(server, options) {
|
|
32
17
|
const { ddnsRepo, dnsProvider, defaultDomain } = options;
|
|
33
18
|
/**
|
|
@@ -61,38 +46,25 @@ function registerDdnsRoutes(server, options) {
|
|
|
61
46
|
sendError(response, 409, 'Subdomain already allocated');
|
|
62
47
|
return;
|
|
63
48
|
}
|
|
64
|
-
const mode = payload.mode === 'tunnel' ? 'tunnel' : 'direct';
|
|
65
|
-
const recordType = pickRecordType({
|
|
66
|
-
mode,
|
|
67
|
-
ipAddress: payload.ipAddress,
|
|
68
|
-
ipv6Address: payload.ipv6Address,
|
|
69
|
-
});
|
|
70
|
-
const recordValue = pickRecordValue({
|
|
71
|
-
subdomain: payload.subdomain,
|
|
72
|
-
mode,
|
|
73
|
-
tunnelProvider: payload.tunnelProvider,
|
|
74
|
-
ipAddress: payload.ipAddress,
|
|
75
|
-
ipv6Address: payload.ipv6Address,
|
|
76
|
-
});
|
|
77
49
|
const record = await ddnsRepo.allocateSubdomain({
|
|
78
50
|
subdomain: payload.subdomain,
|
|
79
51
|
domain: defaultDomain,
|
|
80
52
|
nodeId: payload.nodeId,
|
|
81
53
|
username: payload.username,
|
|
82
|
-
ipAddress:
|
|
83
|
-
ipv6Address:
|
|
84
|
-
recordType,
|
|
54
|
+
ipAddress: payload.ipAddress,
|
|
55
|
+
ipv6Address: payload.ipv6Address,
|
|
85
56
|
});
|
|
86
|
-
|
|
57
|
+
// 如果有 DNS Provider 且有 IP,创建 DNS 记录
|
|
58
|
+
if (dnsProvider && (payload.ipAddress || payload.ipv6Address)) {
|
|
87
59
|
try {
|
|
88
60
|
await dnsProvider.upsertRecord({
|
|
89
61
|
domain: defaultDomain,
|
|
90
62
|
subdomain: payload.subdomain,
|
|
91
|
-
type:
|
|
92
|
-
value:
|
|
63
|
+
type: payload.ipv6Address ? 'AAAA' : 'A',
|
|
64
|
+
value: payload.ipv6Address ?? payload.ipAddress,
|
|
93
65
|
ttl: 60,
|
|
94
66
|
});
|
|
95
|
-
logger.info(`Created DNS record: ${payload.subdomain}.${defaultDomain}
|
|
67
|
+
logger.info(`Created DNS record: ${payload.subdomain}.${defaultDomain}`);
|
|
96
68
|
}
|
|
97
69
|
catch (dnsError) {
|
|
98
70
|
logger.error(`Failed to create DNS record: ${dnsError}`);
|
|
@@ -107,6 +79,7 @@ function registerDdnsRoutes(server, options) {
|
|
|
107
79
|
fqdn: `${record.subdomain}.${record.domain}`,
|
|
108
80
|
ipAddress: record.ipAddress,
|
|
109
81
|
ipv6Address: record.ipv6Address,
|
|
82
|
+
tunnelProvider: payload.tunnelProvider,
|
|
110
83
|
createdAt: record.createdAt.toISOString(),
|
|
111
84
|
});
|
|
112
85
|
}
|
|
@@ -137,31 +110,43 @@ function registerDdnsRoutes(server, options) {
|
|
|
137
110
|
const payload = body;
|
|
138
111
|
const ipAddress = payload?.ip ?? payload?.ipAddress;
|
|
139
112
|
const ipv6Address = payload?.ipv6Address;
|
|
140
|
-
const mode = payload?.mode
|
|
141
|
-
if (
|
|
113
|
+
const mode = payload?.mode ?? 'direct';
|
|
114
|
+
if (!ipAddress && !ipv6Address && mode !== 'tunnel') {
|
|
142
115
|
sendError(response, 400, 'ip or ipv6Address is required');
|
|
143
116
|
return;
|
|
144
117
|
}
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
118
|
+
if (mode === 'tunnel' && !ipAddress && !ipv6Address) {
|
|
119
|
+
const existing = await ddnsRepo.getRecord(subdomain);
|
|
120
|
+
if (!existing) {
|
|
121
|
+
sendError(response, 404, 'Subdomain not found');
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
sendJson(response, 200, {
|
|
125
|
+
success: true,
|
|
126
|
+
subdomain: existing.subdomain,
|
|
127
|
+
domain: existing.domain,
|
|
128
|
+
fqdn: `${existing.subdomain}.${existing.domain}`,
|
|
129
|
+
ipAddress: existing.ipAddress,
|
|
130
|
+
ipv6Address: existing.ipv6Address,
|
|
131
|
+
tunnelProvider: payload?.tunnelProvider,
|
|
132
|
+
updatedAt: existing.updatedAt.toISOString(),
|
|
133
|
+
});
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
153
136
|
// 更新数据库记录
|
|
154
137
|
const record = await ddnsRepo.updateRecordIp(subdomain, {
|
|
155
|
-
ipAddress
|
|
156
|
-
ipv6Address
|
|
157
|
-
recordType,
|
|
138
|
+
ipAddress,
|
|
139
|
+
ipv6Address,
|
|
158
140
|
});
|
|
159
141
|
if (!record) {
|
|
160
142
|
sendError(response, 404, 'Subdomain not found');
|
|
161
143
|
return;
|
|
162
144
|
}
|
|
163
|
-
|
|
145
|
+
// 更新 DNS 记录
|
|
146
|
+
if (dnsProvider) {
|
|
164
147
|
try {
|
|
148
|
+
const recordType = ipv6Address ? 'AAAA' : 'A';
|
|
149
|
+
const recordValue = ipv6Address ?? ipAddress;
|
|
165
150
|
await dnsProvider.upsertRecord({
|
|
166
151
|
domain: record.domain,
|
|
167
152
|
subdomain: record.subdomain,
|
|
@@ -183,6 +168,7 @@ function registerDdnsRoutes(server, options) {
|
|
|
183
168
|
fqdn: `${record.subdomain}.${record.domain}`,
|
|
184
169
|
ipAddress: record.ipAddress,
|
|
185
170
|
ipv6Address: record.ipv6Address,
|
|
171
|
+
tunnelProvider: payload?.tunnelProvider,
|
|
186
172
|
updatedAt: record.updatedAt.toISOString(),
|
|
187
173
|
});
|
|
188
174
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DdnsHandler.js","sourceRoot":"","sources":["../../../src/api/handlers/DdnsHandler.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * DDNS API Handler\n *\n * 提供 DDNS 服务的 HTTP API\n *\n * POST /api/v1/ddns/{subdomain} - 更新 DNS 记录\n * GET /api/v1/ddns/{subdomain} - 查询 DNS 记录\n * POST /api/v1/ddns/allocate - 分配子域名\n * DELETE /api/v1/ddns/{subdomain} - 释放子域名\n */\n\nimport type { ServerResponse, IncomingMessage } from 'node:http';\nimport { getLoggerFor } from 'global-logger-factory';\nimport type { ApiServer } from '../ApiServer';\nimport type { DdnsRepository } from '../../identity/drizzle/DdnsRepository';\nimport type { DnsProvider } from '../../dns/DnsProvider';\n\nconst logger = getLoggerFor('DdnsHandler');\n\nexport interface DdnsHandlerOptions {\n ddnsRepo: DdnsRepository;\n dnsProvider?: DnsProvider;\n defaultDomain: string;\n}\n\ntype DdnsMode = 'direct' | 'tunnel';\ntype DdnsTunnelProvider = 'cloudflare' | 'sakura_frp' | 'none';\n\nfunction pickRecordType(options: {\n mode: DdnsMode;\n ipAddress?: string;\n ipv6Address?: string;\n}): 'A' | 'AAAA' | 'CNAME' {\n if (options.mode === 'tunnel') {\n return 'CNAME';\n }\n return options.ipv6Address ? 'AAAA' : 'A';\n}\n\nfunction pickRecordValue(options: {\n subdomain: string;\n mode: DdnsMode;\n tunnelProvider?: DdnsTunnelProvider;\n ipAddress?: string;\n ipv6Address?: string;\n}): string | undefined {\n if (options.mode === 'tunnel') {\n if (options.tunnelProvider === 'cloudflare') {\n return `${options.subdomain}.cfargotunnel.com`;\n }\n return undefined;\n }\n return options.ipv6Address ?? options.ipAddress;\n}\n\nexport function registerDdnsRoutes(\n server: ApiServer,\n options: DdnsHandlerOptions,\n): void {\n const { ddnsRepo, dnsProvider, defaultDomain } = options;\n\n /**\n * POST /api/v1/ddns/allocate\n *\n * 分配子域名\n *\n * Request body:\n * {\n * \"subdomain\": \"alice\",\n * \"nodeId\": \"node-xxx\", // optional\n * \"ipAddress\": \"1.2.3.4\" // optional\n * }\n */\n server.post('/api/v1/ddns/allocate', async (request, response, _params) => {\n try {\n const body = await readJsonBody(request);\n const payload = body as {\n subdomain?: string;\n nodeId?: string;\n username?: string;\n ipAddress?: string;\n ipv6Address?: string;\n mode?: DdnsMode;\n tunnelProvider?: DdnsTunnelProvider;\n } | undefined;\n\n if (!payload?.subdomain) {\n sendError(response, 400, 'subdomain is required');\n return;\n }\n\n // 验证子域名格式\n if (!/^[a-z0-9][a-z0-9-]{0,61}[a-z0-9]$/.test(payload.subdomain)) {\n sendError(response, 400, 'Invalid subdomain format');\n return;\n }\n\n // 检查是否已存在\n const existing = await ddnsRepo.getRecord(payload.subdomain);\n if (existing) {\n sendError(response, 409, 'Subdomain already allocated');\n return;\n }\n\n const mode = payload.mode === 'tunnel' ? 'tunnel' : 'direct';\n const recordType = pickRecordType({\n mode,\n ipAddress: payload.ipAddress,\n ipv6Address: payload.ipv6Address,\n });\n const recordValue = pickRecordValue({\n subdomain: payload.subdomain,\n mode,\n tunnelProvider: payload.tunnelProvider,\n ipAddress: payload.ipAddress,\n ipv6Address: payload.ipv6Address,\n });\n\n const record = await ddnsRepo.allocateSubdomain({\n subdomain: payload.subdomain,\n domain: defaultDomain,\n nodeId: payload.nodeId,\n username: payload.username,\n ipAddress: mode === 'direct' ? payload.ipAddress : undefined,\n ipv6Address: mode === 'direct' ? payload.ipv6Address : undefined,\n recordType,\n });\n\n if (dnsProvider && recordValue) {\n try {\n await dnsProvider.upsertRecord({\n domain: defaultDomain,\n subdomain: payload.subdomain,\n type: recordType,\n value: recordValue,\n ttl: 60,\n });\n logger.info(`Created DNS record: ${payload.subdomain}.${defaultDomain} (${recordType})`);\n } catch (dnsError) {\n logger.error(`Failed to create DNS record: ${dnsError}`);\n // 不回滚数据库记录,DNS 可以稍后重试\n }\n }\n\n logger.info(`Allocated subdomain: ${payload.subdomain}.${defaultDomain}`);\n\n sendJson(response, 201, {\n success: true,\n subdomain: record.subdomain,\n domain: record.domain,\n fqdn: `${record.subdomain}.${record.domain}`,\n ipAddress: record.ipAddress,\n ipv6Address: record.ipv6Address,\n createdAt: record.createdAt.toISOString(),\n });\n } catch (error) {\n logger.error(`Failed to allocate subdomain: ${error}`);\n sendError(response, 500, 'Internal server error');\n }\n });\n\n /**\n * POST /api/v1/ddns/{subdomain}\n *\n * 更新 DNS 记录\n *\n * Request body:\n * {\n * \"ip\": \"1.2.3.4\",\n * \"type\": \"A\" // optional, default: \"A\"\n * }\n */\n server.post('/api/v1/ddns/:subdomain', async (request, response, params) => {\n const subdomain = decodeURIComponent(params.subdomain);\n\n // 跳过 allocate 路由\n if (subdomain === 'allocate') {\n return;\n }\n\n try {\n const body = await readJsonBody(request);\n const payload = body as {\n ip?: string;\n ipAddress?: string;\n ipv6Address?: string;\n type?: string;\n mode?: DdnsMode;\n tunnelProvider?: DdnsTunnelProvider;\n } | undefined;\n\n const ipAddress = payload?.ip ?? payload?.ipAddress;\n const ipv6Address = payload?.ipv6Address;\n\n const mode = payload?.mode === 'tunnel' ? 'tunnel' : 'direct';\n if (mode === 'direct' && !ipAddress && !ipv6Address) {\n sendError(response, 400, 'ip or ipv6Address is required');\n return;\n }\n\n const recordType = pickRecordType({ mode, ipAddress, ipv6Address });\n const recordValue = pickRecordValue({\n subdomain,\n mode,\n tunnelProvider: payload?.tunnelProvider,\n ipAddress,\n ipv6Address,\n });\n\n // 更新数据库记录\n const record = await ddnsRepo.updateRecordIp(subdomain, {\n ipAddress: mode === 'direct' ? (ipAddress ?? null) : null,\n ipv6Address: mode === 'direct' ? (ipv6Address ?? null) : null,\n recordType,\n });\n\n if (!record) {\n sendError(response, 404, 'Subdomain not found');\n return;\n }\n\n if (dnsProvider && recordValue) {\n try {\n await dnsProvider.upsertRecord({\n domain: record.domain,\n subdomain: record.subdomain,\n type: recordType,\n value: recordValue,\n ttl: record.ttl,\n });\n logger.info(`Updated DNS record: ${subdomain}.${record.domain} -> ${recordValue}`);\n } catch (dnsError) {\n logger.error(`Failed to update DNS record: ${dnsError}`);\n // 数据库已更新,DNS 更新失败不影响响应\n }\n }\n\n sendJson(response, 200, {\n success: true,\n subdomain: record.subdomain,\n domain: record.domain,\n fqdn: `${record.subdomain}.${record.domain}`,\n ipAddress: record.ipAddress,\n ipv6Address: record.ipv6Address,\n updatedAt: record.updatedAt.toISOString(),\n });\n } catch (error) {\n if (error instanceof Error && error.message.includes('banned')) {\n sendError(response, 403, error.message);\n return;\n }\n logger.error(`Failed to update DDNS record: ${error}`);\n sendError(response, 500, 'Internal server error');\n }\n });\n\n /**\n * GET /api/v1/ddns/{subdomain}\n *\n * 查询 DNS 记录\n */\n server.get('/api/v1/ddns/:subdomain', async (_request, response, params) => {\n const subdomain = decodeURIComponent(params.subdomain);\n\n try {\n const record = await ddnsRepo.getRecord(subdomain);\n\n if (!record) {\n sendError(response, 404, 'Subdomain not found');\n return;\n }\n\n sendJson(response, 200, {\n subdomain: record.subdomain,\n domain: record.domain,\n fqdn: `${record.subdomain}.${record.domain}`,\n ipAddress: record.ipAddress,\n ipv6Address: record.ipv6Address,\n recordType: record.recordType,\n status: record.status,\n ttl: record.ttl,\n createdAt: record.createdAt.toISOString(),\n updatedAt: record.updatedAt.toISOString(),\n });\n } catch (error) {\n logger.error(`Failed to get DDNS record: ${error}`);\n sendError(response, 500, 'Internal server error');\n }\n });\n\n /**\n * DELETE /api/v1/ddns/{subdomain}\n *\n * 释放子域名\n */\n server.delete('/api/v1/ddns/:subdomain', async (_request, response, params) => {\n const subdomain = decodeURIComponent(params.subdomain);\n\n try {\n const record = await ddnsRepo.getRecord(subdomain);\n\n if (!record) {\n sendError(response, 404, 'Subdomain not found');\n return;\n }\n\n // 删除 DNS 记录\n if (dnsProvider) {\n try {\n await dnsProvider.deleteRecord({\n domain: record.domain,\n subdomain: record.subdomain,\n type: record.recordType,\n });\n logger.info(`Deleted DNS record: ${subdomain}.${record.domain}`);\n } catch (dnsError) {\n logger.error(`Failed to delete DNS record: ${dnsError}`);\n }\n }\n\n // 删除数据库记录\n await ddnsRepo.releaseSubdomain(subdomain);\n\n logger.info(`Released subdomain: ${subdomain}`);\n\n sendJson(response, 200, {\n success: true,\n message: `Subdomain ${subdomain} released`,\n });\n } catch (error) {\n logger.error(`Failed to release subdomain: ${error}`);\n sendError(response, 500, 'Internal server error');\n }\n });\n\n /**\n * POST /api/v1/ddns/{subdomain}/ban\n *\n * 封禁子域名 (管理员)\n */\n server.post('/api/v1/ddns/:subdomain/ban', async (request, response, params) => {\n const subdomain = decodeURIComponent(params.subdomain);\n\n try {\n const body = await readJsonBody(request);\n const payload = body as { reason?: string } | undefined;\n\n const reason = payload?.reason ?? 'Banned by administrator';\n\n await ddnsRepo.banSubdomain(subdomain, reason);\n\n // 删除 DNS 记录\n if (dnsProvider) {\n const record = await ddnsRepo.getRecord(subdomain);\n if (record) {\n try {\n await dnsProvider.deleteRecord({\n domain: record.domain,\n subdomain: record.subdomain,\n type: record.recordType,\n });\n } catch (dnsError) {\n logger.error(`Failed to delete DNS record for banned subdomain: ${dnsError}`);\n }\n }\n }\n\n logger.warn(`Banned subdomain: ${subdomain}, reason: ${reason}`);\n\n sendJson(response, 200, {\n success: true,\n message: `Subdomain ${subdomain} banned`,\n reason,\n });\n } catch (error) {\n logger.error(`Failed to ban subdomain: ${error}`);\n sendError(response, 500, 'Internal server error');\n }\n });\n\n logger.info('DDNS routes registered');\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":"DdnsHandler.js","sourceRoot":"","sources":["../../../src/api/handlers/DdnsHandler.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * DDNS API Handler\n *\n * 提供 DDNS 服务的 HTTP API\n *\n * POST /api/v1/ddns/{subdomain} - 更新 DNS 记录\n * GET /api/v1/ddns/{subdomain} - 查询 DNS 记录\n * POST /api/v1/ddns/allocate - 分配子域名\n * DELETE /api/v1/ddns/{subdomain} - 释放子域名\n */\n\nimport type { ServerResponse, IncomingMessage } from 'node:http';\nimport { getLoggerFor } from 'global-logger-factory';\nimport type { ApiServer } from '../ApiServer';\nimport type { DdnsRepository } from '../../identity/drizzle/DdnsRepository';\nimport type { DnsProvider } from '../../dns/DnsProvider';\n\nconst logger = getLoggerFor('DdnsHandler');\n\nexport interface DdnsHandlerOptions {\n ddnsRepo: DdnsRepository;\n dnsProvider?: DnsProvider;\n defaultDomain: string;\n}\n\nexport function registerDdnsRoutes(\n server: ApiServer,\n options: DdnsHandlerOptions,\n): void {\n const { ddnsRepo, dnsProvider, defaultDomain } = options;\n\n /**\n * POST /api/v1/ddns/allocate\n *\n * 分配子域名\n *\n * Request body:\n * {\n * \"subdomain\": \"alice\",\n * \"nodeId\": \"node-xxx\", // optional\n * \"ipAddress\": \"1.2.3.4\" // optional\n * }\n */\n server.post('/api/v1/ddns/allocate', async (request, response, _params) => {\n try {\n const body = await readJsonBody(request);\n const payload = body as {\n subdomain?: string;\n nodeId?: string;\n username?: string;\n ipAddress?: string;\n ipv6Address?: string;\n mode?: 'direct' | 'tunnel';\n tunnelProvider?: string;\n } | undefined;\n\n if (!payload?.subdomain) {\n sendError(response, 400, 'subdomain is required');\n return;\n }\n\n // 验证子域名格式\n if (!/^[a-z0-9][a-z0-9-]{0,61}[a-z0-9]$/.test(payload.subdomain)) {\n sendError(response, 400, 'Invalid subdomain format');\n return;\n }\n\n // 检查是否已存在\n const existing = await ddnsRepo.getRecord(payload.subdomain);\n if (existing) {\n sendError(response, 409, 'Subdomain already allocated');\n return;\n }\n\n const record = await ddnsRepo.allocateSubdomain({\n subdomain: payload.subdomain,\n domain: defaultDomain,\n nodeId: payload.nodeId,\n username: payload.username,\n ipAddress: payload.ipAddress,\n ipv6Address: payload.ipv6Address,\n });\n\n // 如果有 DNS Provider 且有 IP,创建 DNS 记录\n if (dnsProvider && (payload.ipAddress || payload.ipv6Address)) {\n try {\n await dnsProvider.upsertRecord({\n domain: defaultDomain,\n subdomain: payload.subdomain,\n type: payload.ipv6Address ? 'AAAA' : 'A',\n value: payload.ipv6Address ?? payload.ipAddress!,\n ttl: 60,\n });\n logger.info(`Created DNS record: ${payload.subdomain}.${defaultDomain}`);\n } catch (dnsError) {\n logger.error(`Failed to create DNS record: ${dnsError}`);\n // 不回滚数据库记录,DNS 可以稍后重试\n }\n }\n\n logger.info(`Allocated subdomain: ${payload.subdomain}.${defaultDomain}`);\n\n sendJson(response, 201, {\n success: true,\n subdomain: record.subdomain,\n domain: record.domain,\n fqdn: `${record.subdomain}.${record.domain}`,\n ipAddress: record.ipAddress,\n ipv6Address: record.ipv6Address,\n tunnelProvider: payload.tunnelProvider,\n createdAt: record.createdAt.toISOString(),\n });\n } catch (error) {\n logger.error(`Failed to allocate subdomain: ${error}`);\n sendError(response, 500, 'Internal server error');\n }\n });\n\n /**\n * POST /api/v1/ddns/{subdomain}\n *\n * 更新 DNS 记录\n *\n * Request body:\n * {\n * \"ip\": \"1.2.3.4\",\n * \"type\": \"A\" // optional, default: \"A\"\n * }\n */\n server.post('/api/v1/ddns/:subdomain', async (request, response, params) => {\n const subdomain = decodeURIComponent(params.subdomain);\n\n // 跳过 allocate 路由\n if (subdomain === 'allocate') {\n return;\n }\n\n try {\n const body = await readJsonBody(request);\n const payload = body as {\n ip?: string;\n ipAddress?: string;\n ipv6Address?: string;\n type?: string;\n mode?: 'direct' | 'tunnel';\n tunnelProvider?: string;\n } | undefined;\n\n const ipAddress = payload?.ip ?? payload?.ipAddress;\n const ipv6Address = payload?.ipv6Address;\n const mode = payload?.mode ?? 'direct';\n\n if (!ipAddress && !ipv6Address && mode !== 'tunnel') {\n sendError(response, 400, 'ip or ipv6Address is required');\n return;\n }\n\n if (mode === 'tunnel' && !ipAddress && !ipv6Address) {\n const existing = await ddnsRepo.getRecord(subdomain);\n\n if (!existing) {\n sendError(response, 404, 'Subdomain not found');\n return;\n }\n\n sendJson(response, 200, {\n success: true,\n subdomain: existing.subdomain,\n domain: existing.domain,\n fqdn: `${existing.subdomain}.${existing.domain}`,\n ipAddress: existing.ipAddress,\n ipv6Address: existing.ipv6Address,\n tunnelProvider: payload?.tunnelProvider,\n updatedAt: existing.updatedAt.toISOString(),\n });\n return;\n }\n\n // 更新数据库记录\n const record = await ddnsRepo.updateRecordIp(subdomain, {\n ipAddress,\n ipv6Address,\n });\n\n if (!record) {\n sendError(response, 404, 'Subdomain not found');\n return;\n }\n\n // 更新 DNS 记录\n if (dnsProvider) {\n try {\n const recordType = ipv6Address ? 'AAAA' : 'A';\n const recordValue = ipv6Address ?? ipAddress!;\n\n await dnsProvider.upsertRecord({\n domain: record.domain,\n subdomain: record.subdomain,\n type: recordType,\n value: recordValue,\n ttl: record.ttl,\n });\n logger.info(`Updated DNS record: ${subdomain}.${record.domain} -> ${recordValue}`);\n } catch (dnsError) {\n logger.error(`Failed to update DNS record: ${dnsError}`);\n // 数据库已更新,DNS 更新失败不影响响应\n }\n }\n\n sendJson(response, 200, {\n success: true,\n subdomain: record.subdomain,\n domain: record.domain,\n fqdn: `${record.subdomain}.${record.domain}`,\n ipAddress: record.ipAddress,\n ipv6Address: record.ipv6Address,\n tunnelProvider: payload?.tunnelProvider,\n updatedAt: record.updatedAt.toISOString(),\n });\n } catch (error) {\n if (error instanceof Error && error.message.includes('banned')) {\n sendError(response, 403, error.message);\n return;\n }\n logger.error(`Failed to update DDNS record: ${error}`);\n sendError(response, 500, 'Internal server error');\n }\n });\n\n /**\n * GET /api/v1/ddns/{subdomain}\n *\n * 查询 DNS 记录\n */\n server.get('/api/v1/ddns/:subdomain', async (_request, response, params) => {\n const subdomain = decodeURIComponent(params.subdomain);\n\n try {\n const record = await ddnsRepo.getRecord(subdomain);\n\n if (!record) {\n sendError(response, 404, 'Subdomain not found');\n return;\n }\n\n sendJson(response, 200, {\n subdomain: record.subdomain,\n domain: record.domain,\n fqdn: `${record.subdomain}.${record.domain}`,\n ipAddress: record.ipAddress,\n ipv6Address: record.ipv6Address,\n recordType: record.recordType,\n status: record.status,\n ttl: record.ttl,\n createdAt: record.createdAt.toISOString(),\n updatedAt: record.updatedAt.toISOString(),\n });\n } catch (error) {\n logger.error(`Failed to get DDNS record: ${error}`);\n sendError(response, 500, 'Internal server error');\n }\n });\n\n /**\n * DELETE /api/v1/ddns/{subdomain}\n *\n * 释放子域名\n */\n server.delete('/api/v1/ddns/:subdomain', async (_request, response, params) => {\n const subdomain = decodeURIComponent(params.subdomain);\n\n try {\n const record = await ddnsRepo.getRecord(subdomain);\n\n if (!record) {\n sendError(response, 404, 'Subdomain not found');\n return;\n }\n\n // 删除 DNS 记录\n if (dnsProvider) {\n try {\n await dnsProvider.deleteRecord({\n domain: record.domain,\n subdomain: record.subdomain,\n type: record.recordType,\n });\n logger.info(`Deleted DNS record: ${subdomain}.${record.domain}`);\n } catch (dnsError) {\n logger.error(`Failed to delete DNS record: ${dnsError}`);\n }\n }\n\n // 删除数据库记录\n await ddnsRepo.releaseSubdomain(subdomain);\n\n logger.info(`Released subdomain: ${subdomain}`);\n\n sendJson(response, 200, {\n success: true,\n message: `Subdomain ${subdomain} released`,\n });\n } catch (error) {\n logger.error(`Failed to release subdomain: ${error}`);\n sendError(response, 500, 'Internal server error');\n }\n });\n\n /**\n * POST /api/v1/ddns/{subdomain}/ban\n *\n * 封禁子域名 (管理员)\n */\n server.post('/api/v1/ddns/:subdomain/ban', async (request, response, params) => {\n const subdomain = decodeURIComponent(params.subdomain);\n\n try {\n const body = await readJsonBody(request);\n const payload = body as { reason?: string } | undefined;\n\n const reason = payload?.reason ?? 'Banned by administrator';\n\n await ddnsRepo.banSubdomain(subdomain, reason);\n\n // 删除 DNS 记录\n if (dnsProvider) {\n const record = await ddnsRepo.getRecord(subdomain);\n if (record) {\n try {\n await dnsProvider.deleteRecord({\n domain: record.domain,\n subdomain: record.subdomain,\n type: record.recordType,\n });\n } catch (dnsError) {\n logger.error(`Failed to delete DNS record for banned subdomain: ${dnsError}`);\n }\n }\n }\n\n logger.warn(`Banned subdomain: ${subdomain}, reason: ${reason}`);\n\n sendJson(response, 200, {\n success: true,\n message: `Subdomain ${subdomain} banned`,\n reason,\n });\n } catch (error) {\n logger.error(`Failed to ban subdomain: ${error}`);\n sendError(response, 500, 'Internal server error');\n }\n });\n\n logger.info('DDNS routes registered');\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"]}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.registerLinxCapabilitiesRoutes = registerLinxCapabilitiesRoutes;
|
|
7
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
8
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
9
|
+
const runtime_1 = require("../../runtime");
|
|
10
|
+
const LINX_LOCAL_ONBOARDING_CONTRACT = 'linx-local-onboarding/v1';
|
|
11
|
+
function registerLinxCapabilitiesRoutes(server) {
|
|
12
|
+
server.get('/api/linx/capabilities', async (_request, response) => {
|
|
13
|
+
sendJson(response, 200, {
|
|
14
|
+
contract: LINX_LOCAL_ONBOARDING_CONTRACT,
|
|
15
|
+
baseUrl: ensureTrailingSlash(process.env.CSS_BASE_URL || 'http://localhost:3000/'),
|
|
16
|
+
version: getVersion(),
|
|
17
|
+
});
|
|
18
|
+
}, { public: true });
|
|
19
|
+
}
|
|
20
|
+
function getVersion() {
|
|
21
|
+
try {
|
|
22
|
+
const packageJsonPath = node_path_1.default.join(runtime_1.PACKAGE_ROOT, 'package.json');
|
|
23
|
+
const pkg = JSON.parse(node_fs_1.default.readFileSync(packageJsonPath, 'utf8'));
|
|
24
|
+
return pkg.version ?? 'unknown';
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
return 'unknown';
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
function ensureTrailingSlash(value) {
|
|
31
|
+
return value.endsWith('/') ? value : `${value}/`;
|
|
32
|
+
}
|
|
33
|
+
function sendJson(response, status, payload) {
|
|
34
|
+
response.statusCode = status;
|
|
35
|
+
response.setHeader('Content-Type', 'application/json');
|
|
36
|
+
response.end(JSON.stringify(payload));
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=LinxCapabilitiesHandler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"LinxCapabilitiesHandler.js","sourceRoot":"","sources":["../../../src/api/handlers/LinxCapabilitiesHandler.ts"],"names":[],"mappings":";;;;;AAQA,wEAQC;AAhBD,sDAAyB;AACzB,0DAA6B;AAG7B,2CAA6C;AAE7C,MAAM,8BAA8B,GAAG,0BAA0B,CAAC;AAElE,SAAgB,8BAA8B,CAAC,MAAiB;IAC9D,MAAM,CAAC,GAAG,CAAC,wBAAwB,EAAE,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,EAAE;QAChE,QAAQ,CAAC,QAAQ,EAAE,GAAG,EAAE;YACtB,QAAQ,EAAE,8BAA8B;YACxC,OAAO,EAAE,mBAAmB,CAAC,OAAO,CAAC,GAAG,CAAC,YAAY,IAAI,wBAAwB,CAAC;YAClF,OAAO,EAAE,UAAU,EAAE;SACtB,CAAC,CAAC;IACL,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;AACvB,CAAC;AAED,SAAS,UAAU;IACjB,IAAI,CAAC;QACH,MAAM,eAAe,GAAG,mBAAI,CAAC,IAAI,CAAC,sBAAY,EAAE,cAAc,CAAC,CAAC;QAChE,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,iBAAE,CAAC,YAAY,CAAC,eAAe,EAAE,MAAM,CAAC,CAAyB,CAAC;QACzF,OAAO,GAAG,CAAC,OAAO,IAAI,SAAS,CAAC;IAClC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC;AAED,SAAS,mBAAmB,CAAC,KAAa;IACxC,OAAO,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,KAAK,GAAG,CAAC;AACnD,CAAC;AAED,SAAS,QAAQ,CAAC,QAAwB,EAAE,MAAc,EAAE,OAAgB;IAC1E,QAAQ,CAAC,UAAU,GAAG,MAAM,CAAC;IAC7B,QAAQ,CAAC,SAAS,CAAC,cAAc,EAAE,kBAAkB,CAAC,CAAC;IACvD,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC;AACxC,CAAC","sourcesContent":["import fs from 'node:fs';\nimport path from 'node:path';\nimport type { ServerResponse } from 'node:http';\nimport type { ApiServer } from '../ApiServer';\nimport { PACKAGE_ROOT } from '../../runtime';\n\nconst LINX_LOCAL_ONBOARDING_CONTRACT = 'linx-local-onboarding/v1';\n\nexport function registerLinxCapabilitiesRoutes(server: ApiServer): void {\n server.get('/api/linx/capabilities', async (_request, response) => {\n sendJson(response, 200, {\n contract: LINX_LOCAL_ONBOARDING_CONTRACT,\n baseUrl: ensureTrailingSlash(process.env.CSS_BASE_URL || 'http://localhost:3000/'),\n version: getVersion(),\n });\n }, { public: true });\n}\n\nfunction getVersion(): string {\n try {\n const packageJsonPath = path.join(PACKAGE_ROOT, 'package.json');\n const pkg = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8')) as { version?: string };\n return pkg.version ?? 'unknown';\n } catch {\n return 'unknown';\n }\n}\n\nfunction ensureTrailingSlash(value: string): string {\n return value.endsWith('/') ? value : `${value}/`;\n}\n\nfunction sendJson(response: ServerResponse, status: number, payload: unknown): void {\n response.statusCode = status;\n response.setHeader('Content-Type', 'application/json');\n response.end(JSON.stringify(payload));\n}\n"]}
|
|
@@ -14,8 +14,12 @@
|
|
|
14
14
|
*/
|
|
15
15
|
import type { ApiServer } from '../ApiServer';
|
|
16
16
|
import type { EdgeNodeRepository } from '../../identity/drizzle/EdgeNodeRepository';
|
|
17
|
+
import type { DdnsRepository } from '../../identity/drizzle/DdnsRepository';
|
|
18
|
+
import type { TunnelProvider } from '../../tunnel/TunnelProvider';
|
|
17
19
|
export interface ProvisionHandlerOptions {
|
|
18
20
|
repository: EdgeNodeRepository;
|
|
21
|
+
ddnsRepo?: DdnsRepository;
|
|
22
|
+
tunnelProvider?: TunnelProvider;
|
|
19
23
|
/** Cloud baseUrl,用于派生 provisionCode 签名密钥 */
|
|
20
24
|
baseUrl: string;
|
|
21
25
|
/** 节点域名根域名,如 "undefineds.site" */
|