@undefineds.co/xpod 0.2.27 → 0.2.28
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.
|
@@ -31,7 +31,17 @@ function registerProvisionRoutes(server, options) {
|
|
|
31
31
|
* SP 注册端点(公开,SP 启动时调用,此时用户可能还没有 Cloud 账号)
|
|
32
32
|
*
|
|
33
33
|
* Request:
|
|
34
|
-
* {
|
|
34
|
+
* {
|
|
35
|
+
* publicUrl: string,
|
|
36
|
+
* nodeId?: string,
|
|
37
|
+
* displayName?: string,
|
|
38
|
+
* ipv4?: string,
|
|
39
|
+
* serviceToken?: string,
|
|
40
|
+
* domainMode?: 'managed' | 'self-managed',
|
|
41
|
+
* spDomain?: string,
|
|
42
|
+
* localPort?: number,
|
|
43
|
+
* tunnelToken?: string
|
|
44
|
+
* }
|
|
35
45
|
*
|
|
36
46
|
* Response 201:
|
|
37
47
|
* { nodeId, nodeToken, serviceToken, provisionCode, spDomain? }
|
|
@@ -64,9 +74,14 @@ function registerProvisionRoutes(server, options) {
|
|
|
64
74
|
nodeToken: body.nodeToken,
|
|
65
75
|
serviceToken: body.serviceToken,
|
|
66
76
|
});
|
|
67
|
-
const
|
|
68
|
-
|
|
69
|
-
|
|
77
|
+
const domainMode = body.domainMode === 'self-managed' ? 'self-managed' : 'managed';
|
|
78
|
+
const requestedManagedDomain = normalizeRequestedManagedDomain(body.spDomain, baseStorageDomain);
|
|
79
|
+
const subdomainPrefix = resolveManagedSubdomainPrefix({
|
|
80
|
+
domainMode,
|
|
81
|
+
baseStorageDomain,
|
|
82
|
+
requestedManagedDomain,
|
|
83
|
+
nodeId: result.nodeId,
|
|
84
|
+
});
|
|
70
85
|
const spDomain = subdomainPrefix
|
|
71
86
|
? `${subdomainPrefix}.${baseStorageDomain}`
|
|
72
87
|
: undefined;
|
|
@@ -378,4 +393,29 @@ function sendJson(response, status, data) {
|
|
|
378
393
|
response.setHeader('Content-Type', 'application/json');
|
|
379
394
|
response.end(JSON.stringify(data));
|
|
380
395
|
}
|
|
396
|
+
function normalizeRequestedManagedDomain(value, baseStorageDomain) {
|
|
397
|
+
if (!value || !baseStorageDomain) {
|
|
398
|
+
return undefined;
|
|
399
|
+
}
|
|
400
|
+
const domain = value.trim().toLowerCase().replace(/^https?:\/\//u, '').replace(/\/.*$/u, '').replace(/\.$/u, '');
|
|
401
|
+
const suffix = `.${baseStorageDomain.toLowerCase()}`;
|
|
402
|
+
if (!domain.endsWith(suffix)) {
|
|
403
|
+
return undefined;
|
|
404
|
+
}
|
|
405
|
+
const prefix = domain.slice(0, -suffix.length).replace(/[^a-z0-9-]/giu, '').slice(0, 63);
|
|
406
|
+
if (!prefix) {
|
|
407
|
+
return undefined;
|
|
408
|
+
}
|
|
409
|
+
return `${prefix}.${baseStorageDomain}`;
|
|
410
|
+
}
|
|
411
|
+
function resolveManagedSubdomainPrefix(options) {
|
|
412
|
+
if (options.domainMode !== 'managed' || !options.baseStorageDomain) {
|
|
413
|
+
return undefined;
|
|
414
|
+
}
|
|
415
|
+
if (options.requestedManagedDomain) {
|
|
416
|
+
const suffix = `.${options.baseStorageDomain}`;
|
|
417
|
+
return options.requestedManagedDomain.slice(0, -suffix.length);
|
|
418
|
+
}
|
|
419
|
+
return options.nodeId.replace(/[^a-z0-9-]/gi, '').toLowerCase().slice(0, 63) || options.nodeId.split('-')[0];
|
|
420
|
+
}
|
|
381
421
|
//# sourceMappingURL=ProvisionHandler.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ProvisionHandler.js","sourceRoot":"","sources":["../../../src/api/handlers/ProvisionHandler.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Provision Handler\n *\n * Cloud 端的 SP 注册 API\n *\n * POST /provision/nodes - SP 注册(公开,无需认证)\n * 返回 nodeId、nodeToken、serviceToken、provisionCode(自包含 JWT)\n *\n * provisionCode 是自包含 token,编码了 SP 的 publicUrl 和 serviceToken。\n * CSS 侧的 ProvisionPodCreator 解码后直接回调 SP,不需要查数据库。\n *\n * GET /provision/status - Local 端 SP 状态查询(公开)\n * 返回 SP 配置状态,供 Linx 查询\n */\n\nimport type { ServerResponse, IncomingMessage } from 'node:http';\nimport { getLoggerFor } from 'global-logger-factory';\nimport type { ApiServer } from '../ApiServer';\nimport type { EdgeNodeRepository } from '../../identity/drizzle/EdgeNodeRepository';\nimport type { DdnsRepository } from '../../identity/drizzle/DdnsRepository';\nimport type { DnsProvider } from '../../dns/DnsProvider';\nimport type { TunnelProvider, TunnelConfig } from '../../tunnel/TunnelProvider';\nimport { ProvisionCodeCodec } from '../../provision/ProvisionCodeCodec';\n\nexport interface ProvisionHandlerOptions {\n repository: EdgeNodeRepository;\n ddnsRepo?: DdnsRepository;\n dnsProvider?: DnsProvider;\n tunnelProvider?: TunnelProvider;\n /** Cloud baseUrl,用于派生 provisionCode 签名密钥 */\n baseUrl: string;\n /** 节点域名根域名,如 \"undefineds.site\" */\n baseStorageDomain?: string;\n /** provisionCode 有效期(秒),默认 24 小时 */\n provisionCodeTtl?: number;\n}\n\n/** 默认 24 小时 */\nconst DEFAULT_TTL = 24 * 60 * 60;\n\nexport function registerProvisionRoutes(\n server: ApiServer,\n options: ProvisionHandlerOptions,\n): void {\n const logger = getLoggerFor('ProvisionHandler');\n const { repository, baseUrl, baseStorageDomain } = options;\n const ttl = options.provisionCodeTtl ?? DEFAULT_TTL;\n const codec = new ProvisionCodeCodec(baseUrl);\n\n /**\n * POST /provision/nodes\n *\n * SP 注册端点(公开,SP 启动时调用,此时用户可能还没有 Cloud 账号)\n *\n * Request:\n * { publicUrl: string, nodeId?: string, displayName?: string, ipv4?: string, serviceToken?: string }\n *\n * Response 201:\n * { nodeId, nodeToken, serviceToken, provisionCode, spDomain? }\n */\n server.post('/provision/nodes', async (request, response) => {\n let body: {\n publicUrl?: string;\n nodeId?: string;\n nodeToken?: string;\n displayName?: string;\n ipv4?: string;\n serviceToken?: string;\n localPort?: number;\n tunnelToken?: string;\n };\n try {\n body = await readJsonBody(request) as any ?? {};\n } catch {\n sendJson(response, 400, { error: 'Invalid JSON body' });\n return;\n }\n\n if (!body.publicUrl) {\n sendJson(response, 400, { error: 'publicUrl is required' });\n return;\n }\n\n try {\n new URL(body.publicUrl);\n } catch {\n sendJson(response, 400, { error: 'Invalid publicUrl format' });\n return;\n }\n\n try {\n const result = await repository.registerSpNode({\n publicUrl: body.publicUrl,\n displayName: body.displayName,\n nodeId: body.nodeId,\n nodeToken: body.nodeToken,\n serviceToken: body.serviceToken,\n });\n\n const subdomainPrefix = baseStorageDomain\n ? result.nodeId.replace(/[^a-z0-9-]/gi, '').toLowerCase().slice(0, 63) || result.nodeId.split('-')[0]\n : undefined;\n const spDomain = subdomainPrefix\n ? `${subdomainPrefix}.${baseStorageDomain}`\n : undefined;\n const tunnelState = await ensureManagedTunnelState({\n repository,\n nodeId: result.nodeId,\n subdomainPrefix,\n publicUrl: body.publicUrl,\n localPort: body.localPort,\n ipv4: body.ipv4,\n tunnelToken: body.tunnelToken,\n ddnsRepo: options.ddnsRepo,\n dnsProvider: options.dnsProvider,\n tunnelProvider: options.tunnelProvider,\n baseStorageDomain,\n });\n\n if (body.ipv4 || subdomainPrefix) {\n await repository.updateNodeMode(result.nodeId, {\n accessMode: tunnelState?.mode === 'tunnel' ? 'proxy' : 'direct',\n ipv4: body.ipv4,\n subdomain: subdomainPrefix,\n });\n }\n\n // 生成自包含 provisionCode(编码了 SP 信息,CSS 解码后直接回调 SP)\n const provisionCode = codec.encode({\n spUrl: body.publicUrl,\n serviceToken: result.serviceToken,\n nodeId: result.nodeId,\n spDomain,\n exp: Math.floor(Date.now() / 1000) + ttl,\n });\n\n logger.info(`Registered SP node ${result.nodeId} at ${body.publicUrl}${spDomain ? `, spDomain: ${spDomain}` : ''}`);\n\n const responseBody: Record<string, unknown> = {\n nodeId: result.nodeId,\n nodeToken: result.nodeToken,\n serviceToken: result.serviceToken,\n provisionCode,\n };\n if (spDomain) {\n responseBody.spDomain = spDomain;\n }\n if (tunnelState?.tunnelConfig?.tunnelToken) {\n responseBody.tunnelToken = tunnelState.tunnelConfig.tunnelToken;\n }\n if (tunnelState?.tunnelConfig?.provider) {\n responseBody.tunnelProvider = tunnelState.tunnelConfig.provider;\n }\n if (tunnelState?.tunnelConfig?.endpoint) {\n responseBody.tunnelEndpoint = tunnelState.tunnelConfig.endpoint;\n }\n\n sendJson(response, 201, responseBody);\n } catch (error) {\n if (error instanceof InvalidTunnelTokenError) {\n sendJson(response, 400, { error: error.message });\n return;\n }\n logger.error(`Failed to register SP node: ${error}`);\n sendJson(response, 500, { error: 'Failed to register SP node' });\n }\n }, { public: true });\n\n logger.info('Provision routes registered');\n}\n\ninterface ManagedTunnelState {\n mode: 'direct' | 'tunnel';\n tunnelConfig?: TunnelConfig;\n}\n\nclass InvalidTunnelTokenError extends Error {}\n\nasync function ensureManagedTunnelState(options: {\n repository: EdgeNodeRepository;\n ddnsRepo?: DdnsRepository;\n dnsProvider?: DnsProvider;\n tunnelProvider?: TunnelProvider;\n nodeId: string;\n subdomainPrefix?: string;\n baseStorageDomain?: string;\n publicUrl: string;\n localPort?: number;\n ipv4?: string;\n tunnelToken?: string;\n}): Promise<ManagedTunnelState | undefined> {\n const {\n repository,\n ddnsRepo,\n dnsProvider,\n tunnelProvider,\n nodeId,\n subdomainPrefix,\n baseStorageDomain,\n publicUrl,\n localPort,\n ipv4,\n tunnelToken,\n } = options;\n\n if (!subdomainPrefix || !baseStorageDomain) {\n return undefined;\n }\n\n const mode: 'direct' | 'tunnel' = ipv4 ? 'direct' : 'tunnel';\n\n if (mode === 'direct') {\n if (ddnsRepo) {\n const existing = await ddnsRepo.getRecord(subdomainPrefix);\n if (!existing) {\n await ddnsRepo.allocateSubdomain({\n subdomain: subdomainPrefix,\n domain: baseStorageDomain,\n nodeId,\n ipAddress: ipv4,\n });\n }\n }\n\n return { mode };\n }\n\n if (tunnelToken) {\n if (!localPort || localPort <= 0) {\n throw new InvalidTunnelTokenError('localPort is required when tunnelToken is provided');\n }\n\n return {\n mode,\n tunnelConfig: await ensureManagedTokenTunnelState({\n repository,\n ddnsRepo,\n dnsProvider,\n nodeId,\n subdomainPrefix,\n baseStorageDomain,\n publicUrl,\n localPort,\n tunnelToken,\n }),\n };\n }\n\n if (ddnsRepo) {\n const existing = await ddnsRepo.getRecord(subdomainPrefix);\n if (!existing) {\n await ddnsRepo.allocateSubdomain({\n subdomain: subdomainPrefix,\n domain: baseStorageDomain,\n nodeId,\n });\n }\n }\n\n if (!tunnelProvider || !localPort || localPort <= 0) {\n return { mode };\n }\n\n const metadataRecord = await repository.getNodeMetadata(nodeId);\n const metadata = metadataRecord?.metadata as Record<string, unknown> | null;\n const existingTunnel = readManagedTunnelConfig(metadata);\n if (existingTunnel && existingTunnel.subdomain === subdomainPrefix && existingTunnel.localPort === localPort) {\n return {\n mode,\n tunnelConfig: existingTunnel.config,\n };\n }\n\n const tunnelConfig = await tunnelProvider.setup({\n subdomain: subdomainPrefix,\n localPort,\n });\n\n await repository.mergeNodeMetadata(nodeId, {\n managedTunnel: {\n provider: tunnelConfig.provider,\n tunnelId: tunnelConfig.tunnelId,\n tunnelToken: tunnelConfig.tunnelToken,\n endpoint: tunnelConfig.endpoint,\n subdomain: subdomainPrefix,\n localPort,\n configuredAt: new Date().toISOString(),\n },\n publicAddress: tunnelConfig.endpoint || publicUrl,\n });\n\n return {\n mode,\n tunnelConfig,\n };\n}\n\nasync function ensureManagedTokenTunnelState(options: {\n repository: EdgeNodeRepository;\n ddnsRepo?: DdnsRepository;\n dnsProvider?: DnsProvider;\n nodeId: string;\n subdomainPrefix: string;\n baseStorageDomain: string;\n publicUrl: string;\n localPort: number;\n tunnelToken: string;\n}): Promise<TunnelConfig> {\n const {\n repository,\n ddnsRepo,\n dnsProvider,\n nodeId,\n subdomainPrefix,\n baseStorageDomain,\n publicUrl,\n localPort,\n tunnelToken,\n } = options;\n\n const parsed = parseCloudflareTunnelToken(tunnelToken);\n if (!parsed?.tunnelId) {\n throw new InvalidTunnelTokenError('Invalid Cloudflare tunnel token');\n }\n\n const endpoint = `https://${subdomainPrefix}.${baseStorageDomain}`;\n const cnameTarget = `${parsed.tunnelId}.cfargotunnel.com`;\n\n if (ddnsRepo) {\n const existing = await ddnsRepo.getRecord(subdomainPrefix);\n if (!existing) {\n await ddnsRepo.allocateSubdomain({\n subdomain: subdomainPrefix,\n domain: baseStorageDomain,\n nodeId,\n ipAddress: cnameTarget,\n recordType: 'CNAME',\n });\n } else if (\n existing.recordType !== 'CNAME'\n || existing.ipAddress !== cnameTarget\n || existing.ipv6Address\n ) {\n await ddnsRepo.updateRecordIp(subdomainPrefix, {\n ipAddress: cnameTarget,\n ipv6Address: null,\n recordType: 'CNAME',\n });\n }\n }\n\n if (dnsProvider) {\n await dnsProvider.upsertRecord({\n domain: baseStorageDomain,\n subdomain: subdomainPrefix,\n type: 'CNAME',\n value: cnameTarget,\n ttl: 60,\n });\n }\n\n const config: TunnelConfig = {\n provider: 'cloudflare',\n subdomain: subdomainPrefix,\n endpoint,\n tunnelId: parsed.tunnelId,\n tunnelToken,\n };\n\n await repository.mergeNodeMetadata(nodeId, {\n managedTunnel: {\n provider: config.provider,\n tunnelId: config.tunnelId,\n tunnelToken: config.tunnelToken,\n endpoint: config.endpoint,\n subdomain: subdomainPrefix,\n localPort,\n configuredAt: new Date().toISOString(),\n source: 'client-token',\n },\n publicAddress: endpoint || publicUrl,\n });\n\n return config;\n}\n\nfunction parseCloudflareTunnelToken(token: string): { accountId?: string; tunnelId?: string } | undefined {\n const decoded = decodeJsonBase64UrlSegment(token) ?? decodeJsonBase64UrlSegment(token.split('.')[0] ?? '');\n if (!decoded || typeof decoded !== 'object') {\n return undefined;\n }\n\n const value = decoded as Record<string, unknown>;\n return {\n accountId: typeof value.a === 'string' ? value.a : undefined,\n tunnelId: typeof value.t === 'string' ? value.t : undefined,\n };\n}\n\nfunction decodeJsonBase64UrlSegment(segment: string): unknown {\n if (!segment) {\n return undefined;\n }\n\n try {\n const normalized = segment.replace(/-/g, '+').replace(/_/g, '/');\n const padding = normalized.length % 4 === 0 ? '' : '='.repeat(4 - (normalized.length % 4));\n const json = Buffer.from(`${normalized}${padding}`, 'base64').toString('utf8');\n return JSON.parse(json);\n } catch {\n return undefined;\n }\n}\n\nfunction readManagedTunnelConfig(metadata: Record<string, unknown> | null): { subdomain?: string; localPort?: number; config: TunnelConfig } | undefined {\n const raw = metadata?.managedTunnel;\n if (!raw || typeof raw !== 'object') {\n return undefined;\n }\n\n const value = raw as Record<string, unknown>;\n const provider = value.provider;\n const endpoint = value.endpoint;\n const tunnelToken = value.tunnelToken;\n const tunnelId = value.tunnelId;\n const subdomain = typeof value.subdomain === 'string' ? value.subdomain : undefined;\n const localPort = typeof value.localPort === 'number' ? value.localPort : undefined;\n\n if (\n (provider !== 'cloudflare' && provider !== 'frp' && provider !== 'sakura-frp')\n || typeof endpoint !== 'string'\n ) {\n return undefined;\n }\n\n return {\n subdomain,\n localPort,\n config: {\n provider,\n subdomain: subdomain ?? 'local',\n endpoint,\n tunnelId: typeof tunnelId === 'string' ? tunnelId : undefined,\n tunnelToken: typeof tunnelToken === 'string' ? tunnelToken : undefined,\n },\n };\n}\n\n/**\n * Local 端 SP 状态查询路由\n */\nexport interface ProvisionStatusOptions {\n /** Cloud API 端点 */\n cloudUrl?: string;\n /** 节点 ID */\n nodeId?: string;\n /** SP 子域名 */\n spDomain?: string;\n /** Cloud baseUrl,用于拼 provisionUrl */\n cloudBaseUrl?: string;\n /** provisionCode(可选,由环境变量传入) */\n provisionCode?: string;\n}\n\nexport function registerProvisionStatusRoute(\n server: ApiServer,\n options: ProvisionStatusOptions,\n): void {\n const logger = getLoggerFor('ProvisionStatusHandler');\n\n server.get('/provision/status', async (_request, response) => {\n const registered = Boolean(options.nodeId && options.cloudUrl);\n\n const body: Record<string, unknown> = {\n registered,\n };\n\n if (registered) {\n body.cloudUrl = options.cloudUrl;\n body.nodeId = options.nodeId;\n if (options.spDomain) {\n body.spDomain = options.spDomain;\n }\n if (options.cloudBaseUrl) {\n const provisionUrl = options.provisionCode\n ? `${options.cloudBaseUrl.replace(/\\/$/, '')}/.account/?provisionCode=${encodeURIComponent(options.provisionCode)}`\n : `${options.cloudBaseUrl.replace(/\\/$/, '')}/.account/`;\n body.provisionUrl = provisionUrl;\n }\n }\n\n sendJson(response, 200, body);\n }, { public: true });\n\n logger.info('Provision status route 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 (error) {\n reject(error);\n }\n });\n request.on('error', reject);\n });\n}\n\nfunction sendJson(response: ServerResponse, status: number, data: unknown): void {\n response.statusCode = status;\n response.setHeader('Content-Type', 'application/json');\n response.end(JSON.stringify(data));\n}\n"]}
|
|
1
|
+
{"version":3,"file":"ProvisionHandler.js","sourceRoot":"","sources":["../../../src/api/handlers/ProvisionHandler.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Provision Handler\n *\n * Cloud 端的 SP 注册 API\n *\n * POST /provision/nodes - SP 注册(公开,无需认证)\n * 返回 nodeId、nodeToken、serviceToken、provisionCode(自包含 JWT)\n *\n * provisionCode 是自包含 token,编码了 SP 的 publicUrl 和 serviceToken。\n * CSS 侧的 ProvisionPodCreator 解码后直接回调 SP,不需要查数据库。\n *\n * GET /provision/status - Local 端 SP 状态查询(公开)\n * 返回 SP 配置状态,供 Linx 查询\n */\n\nimport type { ServerResponse, IncomingMessage } from 'node:http';\nimport { getLoggerFor } from 'global-logger-factory';\nimport type { ApiServer } from '../ApiServer';\nimport type { EdgeNodeRepository } from '../../identity/drizzle/EdgeNodeRepository';\nimport type { DdnsRepository } from '../../identity/drizzle/DdnsRepository';\nimport type { DnsProvider } from '../../dns/DnsProvider';\nimport type { TunnelProvider, TunnelConfig } from '../../tunnel/TunnelProvider';\nimport { ProvisionCodeCodec } from '../../provision/ProvisionCodeCodec';\n\nexport interface ProvisionHandlerOptions {\n repository: EdgeNodeRepository;\n ddnsRepo?: DdnsRepository;\n dnsProvider?: DnsProvider;\n tunnelProvider?: TunnelProvider;\n /** Cloud baseUrl,用于派生 provisionCode 签名密钥 */\n baseUrl: string;\n /** 节点域名根域名,如 \"undefineds.site\" */\n baseStorageDomain?: string;\n /** provisionCode 有效期(秒),默认 24 小时 */\n provisionCodeTtl?: number;\n}\n\n/** 默认 24 小时 */\nconst DEFAULT_TTL = 24 * 60 * 60;\n\nexport function registerProvisionRoutes(\n server: ApiServer,\n options: ProvisionHandlerOptions,\n): void {\n const logger = getLoggerFor('ProvisionHandler');\n const { repository, baseUrl, baseStorageDomain } = options;\n const ttl = options.provisionCodeTtl ?? DEFAULT_TTL;\n const codec = new ProvisionCodeCodec(baseUrl);\n\n /**\n * POST /provision/nodes\n *\n * SP 注册端点(公开,SP 启动时调用,此时用户可能还没有 Cloud 账号)\n *\n * Request:\n * {\n * publicUrl: string,\n * nodeId?: string,\n * displayName?: string,\n * ipv4?: string,\n * serviceToken?: string,\n * domainMode?: 'managed' | 'self-managed',\n * spDomain?: string,\n * localPort?: number,\n * tunnelToken?: string\n * }\n *\n * Response 201:\n * { nodeId, nodeToken, serviceToken, provisionCode, spDomain? }\n */\n server.post('/provision/nodes', async (request, response) => {\n let body: {\n publicUrl?: string;\n nodeId?: string;\n nodeToken?: string;\n displayName?: string;\n ipv4?: string;\n serviceToken?: string;\n localPort?: number;\n tunnelToken?: string;\n tunnelMode?: 'client';\n domainMode?: 'managed' | 'self-managed';\n spDomain?: string;\n };\n try {\n body = await readJsonBody(request) as any ?? {};\n } catch {\n sendJson(response, 400, { error: 'Invalid JSON body' });\n return;\n }\n\n if (!body.publicUrl) {\n sendJson(response, 400, { error: 'publicUrl is required' });\n return;\n }\n\n try {\n new URL(body.publicUrl);\n } catch {\n sendJson(response, 400, { error: 'Invalid publicUrl format' });\n return;\n }\n\n try {\n const result = await repository.registerSpNode({\n publicUrl: body.publicUrl,\n displayName: body.displayName,\n nodeId: body.nodeId,\n nodeToken: body.nodeToken,\n serviceToken: body.serviceToken,\n });\n\n const domainMode = body.domainMode === 'self-managed' ? 'self-managed' : 'managed';\n const requestedManagedDomain = normalizeRequestedManagedDomain(body.spDomain, baseStorageDomain);\n const subdomainPrefix = resolveManagedSubdomainPrefix({\n domainMode,\n baseStorageDomain,\n requestedManagedDomain,\n nodeId: result.nodeId,\n });\n const spDomain = subdomainPrefix\n ? `${subdomainPrefix}.${baseStorageDomain}`\n : undefined;\n const tunnelState = await ensureManagedTunnelState({\n repository,\n nodeId: result.nodeId,\n subdomainPrefix,\n publicUrl: body.publicUrl,\n localPort: body.localPort,\n ipv4: body.ipv4,\n tunnelToken: body.tunnelToken,\n ddnsRepo: options.ddnsRepo,\n dnsProvider: options.dnsProvider,\n tunnelProvider: options.tunnelProvider,\n baseStorageDomain,\n });\n\n if (body.ipv4 || subdomainPrefix) {\n await repository.updateNodeMode(result.nodeId, {\n accessMode: tunnelState?.mode === 'tunnel' ? 'proxy' : 'direct',\n ipv4: body.ipv4,\n subdomain: subdomainPrefix,\n });\n }\n\n // 生成自包含 provisionCode(编码了 SP 信息,CSS 解码后直接回调 SP)\n const provisionCode = codec.encode({\n spUrl: body.publicUrl,\n serviceToken: result.serviceToken,\n nodeId: result.nodeId,\n spDomain,\n exp: Math.floor(Date.now() / 1000) + ttl,\n });\n\n logger.info(`Registered SP node ${result.nodeId} at ${body.publicUrl}${spDomain ? `, spDomain: ${spDomain}` : ''}`);\n\n const responseBody: Record<string, unknown> = {\n nodeId: result.nodeId,\n nodeToken: result.nodeToken,\n serviceToken: result.serviceToken,\n provisionCode,\n };\n if (spDomain) {\n responseBody.spDomain = spDomain;\n }\n if (tunnelState?.tunnelConfig?.tunnelToken) {\n responseBody.tunnelToken = tunnelState.tunnelConfig.tunnelToken;\n }\n if (tunnelState?.tunnelConfig?.provider) {\n responseBody.tunnelProvider = tunnelState.tunnelConfig.provider;\n }\n if (tunnelState?.tunnelConfig?.endpoint) {\n responseBody.tunnelEndpoint = tunnelState.tunnelConfig.endpoint;\n }\n\n sendJson(response, 201, responseBody);\n } catch (error) {\n if (error instanceof InvalidTunnelTokenError) {\n sendJson(response, 400, { error: error.message });\n return;\n }\n logger.error(`Failed to register SP node: ${error}`);\n sendJson(response, 500, { error: 'Failed to register SP node' });\n }\n }, { public: true });\n\n logger.info('Provision routes registered');\n}\n\ninterface ManagedTunnelState {\n mode: 'direct' | 'tunnel';\n tunnelConfig?: TunnelConfig;\n}\n\nclass InvalidTunnelTokenError extends Error {}\n\nasync function ensureManagedTunnelState(options: {\n repository: EdgeNodeRepository;\n ddnsRepo?: DdnsRepository;\n dnsProvider?: DnsProvider;\n tunnelProvider?: TunnelProvider;\n nodeId: string;\n subdomainPrefix?: string;\n baseStorageDomain?: string;\n publicUrl: string;\n localPort?: number;\n ipv4?: string;\n tunnelToken?: string;\n}): Promise<ManagedTunnelState | undefined> {\n const {\n repository,\n ddnsRepo,\n dnsProvider,\n tunnelProvider,\n nodeId,\n subdomainPrefix,\n baseStorageDomain,\n publicUrl,\n localPort,\n ipv4,\n tunnelToken,\n } = options;\n\n if (!subdomainPrefix || !baseStorageDomain) {\n return undefined;\n }\n\n const mode: 'direct' | 'tunnel' = ipv4 ? 'direct' : 'tunnel';\n\n if (mode === 'direct') {\n if (ddnsRepo) {\n const existing = await ddnsRepo.getRecord(subdomainPrefix);\n if (!existing) {\n await ddnsRepo.allocateSubdomain({\n subdomain: subdomainPrefix,\n domain: baseStorageDomain,\n nodeId,\n ipAddress: ipv4,\n });\n }\n }\n\n return { mode };\n }\n\n if (tunnelToken) {\n if (!localPort || localPort <= 0) {\n throw new InvalidTunnelTokenError('localPort is required when tunnelToken is provided');\n }\n\n return {\n mode,\n tunnelConfig: await ensureManagedTokenTunnelState({\n repository,\n ddnsRepo,\n dnsProvider,\n nodeId,\n subdomainPrefix,\n baseStorageDomain,\n publicUrl,\n localPort,\n tunnelToken,\n }),\n };\n }\n\n if (ddnsRepo) {\n const existing = await ddnsRepo.getRecord(subdomainPrefix);\n if (!existing) {\n await ddnsRepo.allocateSubdomain({\n subdomain: subdomainPrefix,\n domain: baseStorageDomain,\n nodeId,\n });\n }\n }\n\n if (!tunnelProvider || !localPort || localPort <= 0) {\n return { mode };\n }\n\n const metadataRecord = await repository.getNodeMetadata(nodeId);\n const metadata = metadataRecord?.metadata as Record<string, unknown> | null;\n const existingTunnel = readManagedTunnelConfig(metadata);\n if (existingTunnel && existingTunnel.subdomain === subdomainPrefix && existingTunnel.localPort === localPort) {\n return {\n mode,\n tunnelConfig: existingTunnel.config,\n };\n }\n\n const tunnelConfig = await tunnelProvider.setup({\n subdomain: subdomainPrefix,\n localPort,\n });\n\n await repository.mergeNodeMetadata(nodeId, {\n managedTunnel: {\n provider: tunnelConfig.provider,\n tunnelId: tunnelConfig.tunnelId,\n tunnelToken: tunnelConfig.tunnelToken,\n endpoint: tunnelConfig.endpoint,\n subdomain: subdomainPrefix,\n localPort,\n configuredAt: new Date().toISOString(),\n },\n publicAddress: tunnelConfig.endpoint || publicUrl,\n });\n\n return {\n mode,\n tunnelConfig,\n };\n}\n\nasync function ensureManagedTokenTunnelState(options: {\n repository: EdgeNodeRepository;\n ddnsRepo?: DdnsRepository;\n dnsProvider?: DnsProvider;\n nodeId: string;\n subdomainPrefix: string;\n baseStorageDomain: string;\n publicUrl: string;\n localPort: number;\n tunnelToken: string;\n}): Promise<TunnelConfig> {\n const {\n repository,\n ddnsRepo,\n dnsProvider,\n nodeId,\n subdomainPrefix,\n baseStorageDomain,\n publicUrl,\n localPort,\n tunnelToken,\n } = options;\n\n const parsed = parseCloudflareTunnelToken(tunnelToken);\n if (!parsed?.tunnelId) {\n throw new InvalidTunnelTokenError('Invalid Cloudflare tunnel token');\n }\n\n const endpoint = `https://${subdomainPrefix}.${baseStorageDomain}`;\n const cnameTarget = `${parsed.tunnelId}.cfargotunnel.com`;\n\n if (ddnsRepo) {\n const existing = await ddnsRepo.getRecord(subdomainPrefix);\n if (!existing) {\n await ddnsRepo.allocateSubdomain({\n subdomain: subdomainPrefix,\n domain: baseStorageDomain,\n nodeId,\n ipAddress: cnameTarget,\n recordType: 'CNAME',\n });\n } else if (\n existing.recordType !== 'CNAME'\n || existing.ipAddress !== cnameTarget\n || existing.ipv6Address\n ) {\n await ddnsRepo.updateRecordIp(subdomainPrefix, {\n ipAddress: cnameTarget,\n ipv6Address: null,\n recordType: 'CNAME',\n });\n }\n }\n\n if (dnsProvider) {\n await dnsProvider.upsertRecord({\n domain: baseStorageDomain,\n subdomain: subdomainPrefix,\n type: 'CNAME',\n value: cnameTarget,\n ttl: 60,\n });\n }\n\n const config: TunnelConfig = {\n provider: 'cloudflare',\n subdomain: subdomainPrefix,\n endpoint,\n tunnelId: parsed.tunnelId,\n tunnelToken,\n };\n\n await repository.mergeNodeMetadata(nodeId, {\n managedTunnel: {\n provider: config.provider,\n tunnelId: config.tunnelId,\n tunnelToken: config.tunnelToken,\n endpoint: config.endpoint,\n subdomain: subdomainPrefix,\n localPort,\n configuredAt: new Date().toISOString(),\n source: 'client-token',\n },\n publicAddress: endpoint || publicUrl,\n });\n\n return config;\n}\n\nfunction parseCloudflareTunnelToken(token: string): { accountId?: string; tunnelId?: string } | undefined {\n const decoded = decodeJsonBase64UrlSegment(token) ?? decodeJsonBase64UrlSegment(token.split('.')[0] ?? '');\n if (!decoded || typeof decoded !== 'object') {\n return undefined;\n }\n\n const value = decoded as Record<string, unknown>;\n return {\n accountId: typeof value.a === 'string' ? value.a : undefined,\n tunnelId: typeof value.t === 'string' ? value.t : undefined,\n };\n}\n\nfunction decodeJsonBase64UrlSegment(segment: string): unknown {\n if (!segment) {\n return undefined;\n }\n\n try {\n const normalized = segment.replace(/-/g, '+').replace(/_/g, '/');\n const padding = normalized.length % 4 === 0 ? '' : '='.repeat(4 - (normalized.length % 4));\n const json = Buffer.from(`${normalized}${padding}`, 'base64').toString('utf8');\n return JSON.parse(json);\n } catch {\n return undefined;\n }\n}\n\nfunction readManagedTunnelConfig(metadata: Record<string, unknown> | null): { subdomain?: string; localPort?: number; config: TunnelConfig } | undefined {\n const raw = metadata?.managedTunnel;\n if (!raw || typeof raw !== 'object') {\n return undefined;\n }\n\n const value = raw as Record<string, unknown>;\n const provider = value.provider;\n const endpoint = value.endpoint;\n const tunnelToken = value.tunnelToken;\n const tunnelId = value.tunnelId;\n const subdomain = typeof value.subdomain === 'string' ? value.subdomain : undefined;\n const localPort = typeof value.localPort === 'number' ? value.localPort : undefined;\n\n if (\n (provider !== 'cloudflare' && provider !== 'frp' && provider !== 'sakura-frp')\n || typeof endpoint !== 'string'\n ) {\n return undefined;\n }\n\n return {\n subdomain,\n localPort,\n config: {\n provider,\n subdomain: subdomain ?? 'local',\n endpoint,\n tunnelId: typeof tunnelId === 'string' ? tunnelId : undefined,\n tunnelToken: typeof tunnelToken === 'string' ? tunnelToken : undefined,\n },\n };\n}\n\n/**\n * Local 端 SP 状态查询路由\n */\nexport interface ProvisionStatusOptions {\n /** Cloud API 端点 */\n cloudUrl?: string;\n /** 节点 ID */\n nodeId?: string;\n /** SP 子域名 */\n spDomain?: string;\n /** Cloud baseUrl,用于拼 provisionUrl */\n cloudBaseUrl?: string;\n /** provisionCode(可选,由环境变量传入) */\n provisionCode?: string;\n}\n\nexport function registerProvisionStatusRoute(\n server: ApiServer,\n options: ProvisionStatusOptions,\n): void {\n const logger = getLoggerFor('ProvisionStatusHandler');\n\n server.get('/provision/status', async (_request, response) => {\n const registered = Boolean(options.nodeId && options.cloudUrl);\n\n const body: Record<string, unknown> = {\n registered,\n };\n\n if (registered) {\n body.cloudUrl = options.cloudUrl;\n body.nodeId = options.nodeId;\n if (options.spDomain) {\n body.spDomain = options.spDomain;\n }\n if (options.cloudBaseUrl) {\n const provisionUrl = options.provisionCode\n ? `${options.cloudBaseUrl.replace(/\\/$/, '')}/.account/?provisionCode=${encodeURIComponent(options.provisionCode)}`\n : `${options.cloudBaseUrl.replace(/\\/$/, '')}/.account/`;\n body.provisionUrl = provisionUrl;\n }\n }\n\n sendJson(response, 200, body);\n }, { public: true });\n\n logger.info('Provision status route 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 (error) {\n reject(error);\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 normalizeRequestedManagedDomain(value: string | undefined, baseStorageDomain: string | undefined): string | undefined {\n if (!value || !baseStorageDomain) {\n return undefined;\n }\n\n const domain = value.trim().toLowerCase().replace(/^https?:\\/\\//u, '').replace(/\\/.*$/u, '').replace(/\\.$/u, '');\n const suffix = `.${baseStorageDomain.toLowerCase()}`;\n if (!domain.endsWith(suffix)) {\n return undefined;\n }\n\n const prefix = domain.slice(0, -suffix.length).replace(/[^a-z0-9-]/giu, '').slice(0, 63);\n if (!prefix) {\n return undefined;\n }\n\n return `${prefix}.${baseStorageDomain}`;\n}\n\nfunction resolveManagedSubdomainPrefix(options: {\n domainMode: 'managed' | 'self-managed';\n baseStorageDomain?: string;\n requestedManagedDomain?: string;\n nodeId: string;\n}): string | undefined {\n if (options.domainMode !== 'managed' || !options.baseStorageDomain) {\n return undefined;\n }\n\n if (options.requestedManagedDomain) {\n const suffix = `.${options.baseStorageDomain}`;\n return options.requestedManagedDomain.slice(0, -suffix.length);\n }\n\n return options.nodeId.replace(/[^a-z0-9-]/gi, '').toLowerCase().slice(0, 63) || options.nodeId.split('-')[0];\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.28",
|
|
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",
|