@undefineds.co/xpod 0.3.29 → 0.3.32
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api/auth/AuthContext.d.ts +3 -2
- package/dist/api/auth/AuthContext.js +2 -1
- package/dist/api/auth/AuthContext.js.map +1 -1
- package/dist/api/auth/ClientCredentialsAuthenticator.d.ts +2 -12
- package/dist/api/auth/ClientCredentialsAuthenticator.js +4 -4
- package/dist/api/auth/ClientCredentialsAuthenticator.js.map +1 -1
- package/dist/api/auth/ServiceTokenAuthenticator.d.ts +2 -2
- package/dist/api/auth/ServiceTokenAuthenticator.js.map +1 -1
- package/dist/api/container/business-token.d.ts +1 -1
- package/dist/api/container/business-token.js +5 -1
- package/dist/api/container/business-token.js.map +1 -1
- package/dist/api/container/common.js +14 -10
- package/dist/api/container/common.js.map +1 -1
- package/dist/api/container/routes.js +16 -3
- package/dist/api/container/routes.js.map +1 -1
- package/dist/api/container/types.d.ts +2 -4
- package/dist/api/container/types.js.map +1 -1
- package/dist/api/handlers/ChatHandler.d.ts +1 -1
- package/dist/api/handlers/ChatHandler.js +1 -1
- package/dist/api/handlers/ChatHandler.js.map +1 -1
- package/dist/api/handlers/EdgeNodeSignalHandler.js +3 -1
- package/dist/api/handlers/EdgeNodeSignalHandler.js.map +1 -1
- package/dist/api/handlers/PodManagementHandler.d.ts +2 -0
- package/dist/api/handlers/PodManagementHandler.js +114 -12
- package/dist/api/handlers/PodManagementHandler.js.map +1 -1
- package/dist/api/handlers/ProvisionHandler.d.ts +27 -0
- package/dist/api/handlers/ProvisionHandler.js +339 -32
- package/dist/api/handlers/ProvisionHandler.js.map +1 -1
- package/dist/api/handlers/QuotaHandler.js +0 -12
- package/dist/api/handlers/QuotaHandler.js.map +1 -1
- package/dist/api/handlers/index.d.ts +0 -1
- package/dist/api/handlers/index.js +0 -1
- package/dist/api/handlers/index.js.map +1 -1
- package/dist/api/runtime.js +3 -3
- package/dist/api/runtime.js.map +1 -1
- package/dist/authorization/PodAuthorizationResources.d.ts +1 -0
- package/dist/authorization/PodAuthorizationResources.js +36 -4
- package/dist/authorization/PodAuthorizationResources.js.map +1 -1
- package/dist/components/context.jsonld +12 -0
- package/dist/edge/EdgeNodeAgent.d.ts +1 -1
- package/dist/edge/EdgeNodeAgent.js +1 -1
- package/dist/edge/EdgeNodeAgent.js.map +1 -1
- package/dist/edge/EdgeNodeDnsCoordinator.d.ts +1 -0
- package/dist/edge/EdgeNodeDnsCoordinator.js +9 -3
- package/dist/edge/EdgeNodeDnsCoordinator.js.map +1 -1
- package/dist/edge/EdgeNodeDnsCoordinator.jsonld +4 -0
- package/dist/edge/EdgeNodeHealthProbeService.d.ts +3 -0
- package/dist/edge/EdgeNodeHealthProbeService.js +22 -2
- package/dist/edge/EdgeNodeHealthProbeService.js.map +1 -1
- package/dist/edge/EdgeNodeHealthProbeService.jsonld +12 -0
- package/dist/http/ClusterIngressRouter.js +6 -3
- package/dist/http/ClusterIngressRouter.js.map +1 -1
- package/dist/http/ClusterWebSocketConfigurator.js +6 -2
- package/dist/http/ClusterWebSocketConfigurator.js.map +1 -1
- package/dist/http/EdgeNodeDirectDebugHttpHandler.d.ts +2 -0
- package/dist/http/EdgeNodeDirectDebugHttpHandler.js +18 -3
- package/dist/http/EdgeNodeDirectDebugHttpHandler.js.map +1 -1
- package/dist/http/EdgeNodeDirectDebugHttpHandler.jsonld +8 -0
- package/dist/http/EdgeNodeProxyHttpHandler.js +6 -2
- package/dist/http/EdgeNodeProxyHttpHandler.js.map +1 -1
- package/dist/http/cluster/PodMigrationHttpHandler.d.ts +2 -2
- package/dist/http/cluster/PodMigrationHttpHandler.js +2 -2
- package/dist/http/cluster/PodMigrationHttpHandler.js.map +1 -1
- package/dist/http/quota/QuotaAdminHttpHandler.js +27 -21
- package/dist/http/quota/QuotaAdminHttpHandler.js.map +1 -1
- package/dist/identity/drizzle/AccountRepository.d.ts +4 -22
- package/dist/identity/drizzle/AccountRepository.js +9 -113
- package/dist/identity/drizzle/AccountRepository.js.map +1 -1
- package/dist/identity/drizzle/AccountRoleRepository.d.ts +5 -5
- package/dist/identity/drizzle/AccountRoleRepository.js +204 -97
- package/dist/identity/drizzle/AccountRoleRepository.js.map +1 -1
- package/dist/identity/drizzle/DdnsRepository.d.ts +5 -20
- package/dist/identity/drizzle/DdnsRepository.js +13 -49
- package/dist/identity/drizzle/DdnsRepository.js.map +1 -1
- package/dist/identity/drizzle/EdgeNodeRepository.d.ts +13 -6
- package/dist/identity/drizzle/EdgeNodeRepository.js +167 -66
- package/dist/identity/drizzle/EdgeNodeRepository.js.map +1 -1
- package/dist/identity/drizzle/PodLookupRepository.d.ts +7 -36
- package/dist/identity/drizzle/PodLookupRepository.js +103 -126
- package/dist/identity/drizzle/PodLookupRepository.js.map +1 -1
- package/dist/identity/drizzle/ServiceTokenRepository.d.ts +13 -1
- package/dist/identity/drizzle/ServiceTokenRepository.js +7 -0
- package/dist/identity/drizzle/ServiceTokenRepository.js.map +1 -1
- package/dist/identity/drizzle/db.d.ts +2 -1
- package/dist/identity/drizzle/db.js +173 -297
- package/dist/identity/drizzle/db.js.map +1 -1
- package/dist/identity/drizzle/schema.pg.d.ts +3 -11
- package/dist/identity/drizzle/schema.pg.js +10 -45
- package/dist/identity/drizzle/schema.pg.js.map +1 -1
- package/dist/identity/drizzle/schema.sqlite.d.ts +88 -531
- package/dist/identity/drizzle/schema.sqlite.js +13 -46
- package/dist/identity/drizzle/schema.sqlite.js.map +1 -1
- package/dist/identity/oidc/ScopedPickWebIdHandler.d.ts +3 -0
- package/dist/identity/oidc/ScopedPickWebIdHandler.js +18 -6
- package/dist/identity/oidc/ScopedPickWebIdHandler.js.map +1 -1
- package/dist/identity/oidc/ScopedPickWebIdHandler.jsonld +22 -0
- package/dist/provision/LocalPodProvisioningService.js +2 -0
- package/dist/provision/LocalPodProvisioningService.js.map +1 -1
- package/dist/provision/ProvisionCodeCodec.js +10 -1
- package/dist/provision/ProvisionCodeCodec.js.map +1 -1
- package/dist/provision/ProvisionPodCreator.d.ts +8 -2
- package/dist/provision/ProvisionPodCreator.js +136 -27
- package/dist/provision/ProvisionPodCreator.js.map +1 -1
- package/dist/provision/ProvisionPodCreator.jsonld +38 -3
- package/dist/quota/DrizzleQuotaService.d.ts +0 -4
- package/dist/quota/DrizzleQuotaService.js +1 -21
- package/dist/quota/DrizzleQuotaService.js.map +1 -1
- package/dist/quota/DrizzleQuotaService.jsonld +0 -16
- package/dist/quota/NoopQuotaService.d.ts +0 -4
- package/dist/quota/NoopQuotaService.js +0 -8
- package/dist/quota/NoopQuotaService.js.map +1 -1
- package/dist/quota/NoopQuotaService.jsonld +0 -16
- package/dist/quota/QuotaService.d.ts +0 -4
- package/dist/quota/QuotaService.js.map +1 -1
- package/dist/quota/QuotaService.jsonld +0 -16
- package/dist/service/EdgeNodeSignalClient.d.ts +0 -2
- package/dist/service/EdgeNodeSignalClient.js +0 -4
- package/dist/service/EdgeNodeSignalClient.js.map +1 -1
- package/dist/service/PodMigrationService.d.ts +2 -2
- package/dist/service/PodMigrationService.js +4 -4
- package/dist/service/PodMigrationService.js.map +1 -1
- package/dist/setup/LocalSetupServiceTokenRepository.d.ts +22 -0
- package/dist/setup/LocalSetupServiceTokenRepository.js +68 -0
- package/dist/setup/LocalSetupServiceTokenRepository.js.map +1 -0
- package/dist/storage/accessors/MixDataAccessor.js.map +1 -1
- package/dist/storage/quota/PerAccountQuotaStrategy.js +2 -2
- package/dist/storage/quota/PerAccountQuotaStrategy.js.map +1 -1
- package/dist/storage/quota/UsageRepository.d.ts +10 -32
- package/dist/storage/quota/UsageRepository.js +84 -281
- package/dist/storage/quota/UsageRepository.js.map +1 -1
- package/dist/storage/rdf/PostgresRdfEngine.d.ts +12 -15
- package/dist/storage/rdf/PostgresRdfEngine.js +1040 -150
- package/dist/storage/rdf/PostgresRdfEngine.js.map +1 -1
- package/dist/storage/rdf/PostgresRdfEngine.jsonld +40 -52
- package/dist/storage/rdf/{RdfLocalQueryEngine.d.ts → RdfQueryExecutor.d.ts} +3 -3
- package/dist/storage/rdf/{RdfLocalQueryEngine.js → RdfQueryExecutor.js} +9 -9
- package/dist/storage/rdf/RdfQueryExecutor.js.map +1 -0
- package/dist/storage/rdf/RdfSparqlAdapter.d.ts +5 -5
- package/dist/storage/rdf/RdfSparqlAdapter.js +27 -27
- package/dist/storage/rdf/RdfSparqlAdapter.js.map +1 -1
- package/dist/storage/rdf/SolidRdfEngine.d.ts +2 -5
- package/dist/storage/rdf/SolidRdfEngine.js +6 -38
- package/dist/storage/rdf/SolidRdfEngine.js.map +1 -1
- package/dist/storage/rdf/SolidRdfEngine.jsonld +0 -12
- package/dist/storage/rdf/SolidRdfSparqlEngine.js.map +1 -1
- package/dist/storage/rdf/index.d.ts +3 -3
- package/dist/storage/rdf/index.js +6 -6
- package/dist/storage/rdf/index.js.map +1 -1
- package/dist/storage/rdf/models-benchmark.d.ts +9 -9
- package/dist/storage/rdf/models-benchmark.js +23 -23
- package/dist/storage/rdf/models-benchmark.js.map +1 -1
- package/dist/storage/rdf/types.d.ts +5 -5
- package/dist/storage/rdf/types.js.map +1 -1
- package/dist/subdomain/SubdomainService.d.ts +1 -1
- package/dist/subdomain/SubdomainService.js +1 -1
- package/dist/subdomain/SubdomainService.js.map +1 -1
- package/dist/subdomain/SubdomainService.jsonld +1 -1
- package/package.json +1 -1
- package/templates/pod/acp/profile/.acr +21 -0
- package/templates/pod/wac/profile/.acl.hbs +18 -0
- package/dist/api/handlers/ApiKeyHandler.d.ts +0 -15
- package/dist/api/handlers/ApiKeyHandler.js +0 -153
- package/dist/api/handlers/ApiKeyHandler.js.map +0 -1
- package/dist/api/store/DrizzleClientCredentialsStore.d.ts +0 -51
- package/dist/api/store/DrizzleClientCredentialsStore.js +0 -115
- package/dist/api/store/DrizzleClientCredentialsStore.js.map +0 -1
- package/dist/storage/rdf/RdfLocalQueryEngine.js.map +0 -1
|
@@ -13,13 +13,76 @@ exports.ProvisionPodCreator = void 0;
|
|
|
13
13
|
const global_logger_factory_1 = require("global-logger-factory");
|
|
14
14
|
const community_server_1 = require("@solid/community-server");
|
|
15
15
|
const ProvisionCodeCodec_1 = require("./ProvisionCodeCodec");
|
|
16
|
-
const PodLookupRepository_1 = require("../identity/drizzle/PodLookupRepository");
|
|
17
|
-
const db_1 = require("../identity/drizzle/db");
|
|
18
16
|
function joinUrlPath(baseUrl, relativePath) {
|
|
19
17
|
const normalizedBaseUrl = baseUrl.replace(/\/+$/u, '');
|
|
20
18
|
const normalizedRelativePath = relativePath.replace(/^\/+/u, '');
|
|
21
19
|
return `${normalizedBaseUrl}/${normalizedRelativePath}`;
|
|
22
20
|
}
|
|
21
|
+
function normalizeOptionalUrl(url) {
|
|
22
|
+
const trimmed = url?.trim();
|
|
23
|
+
return trimmed ? trimmed : undefined;
|
|
24
|
+
}
|
|
25
|
+
function normalizeOptionalString(value) {
|
|
26
|
+
const trimmed = value?.trim();
|
|
27
|
+
return trimmed ? trimmed : undefined;
|
|
28
|
+
}
|
|
29
|
+
function normalizeUrlRoot(url) {
|
|
30
|
+
if (!url) {
|
|
31
|
+
return undefined;
|
|
32
|
+
}
|
|
33
|
+
try {
|
|
34
|
+
const parsed = new URL(url);
|
|
35
|
+
parsed.pathname = parsed.pathname.replace(/\/+$/u, '') || '/';
|
|
36
|
+
parsed.search = '';
|
|
37
|
+
parsed.hash = '';
|
|
38
|
+
return parsed.toString();
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
return undefined;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
function isSameUrlRoot(left, right) {
|
|
45
|
+
const normalizedLeft = normalizeUrlRoot(left);
|
|
46
|
+
const normalizedRight = normalizeUrlRoot(right);
|
|
47
|
+
return Boolean(normalizedLeft && normalizedRight && normalizedLeft === normalizedRight);
|
|
48
|
+
}
|
|
49
|
+
function isSameNodeId(left, right) {
|
|
50
|
+
return Boolean(left && right && left.trim() === right.trim());
|
|
51
|
+
}
|
|
52
|
+
function buildDefaultWebId(issuer, podName, relativeWebIdPath) {
|
|
53
|
+
const normalizedRelativePath = relativeWebIdPath.replace(/^\/+/u, '');
|
|
54
|
+
return joinUrlPath(issuer, `${encodeURIComponent(podName)}/${normalizedRelativePath}`);
|
|
55
|
+
}
|
|
56
|
+
function buildStorageRoot(payload) {
|
|
57
|
+
return payload.spDomain ? `https://${payload.spDomain}` : payload.spUrl;
|
|
58
|
+
}
|
|
59
|
+
function buildPodUrl(storageRoot, podName) {
|
|
60
|
+
return joinUrlPath(storageRoot, `${encodeURIComponent(podName)}/`);
|
|
61
|
+
}
|
|
62
|
+
function stripProvisionCode(settings) {
|
|
63
|
+
if (!settings) {
|
|
64
|
+
return undefined;
|
|
65
|
+
}
|
|
66
|
+
const { provisionCode: _provisionCode, ...rest } = settings;
|
|
67
|
+
return rest;
|
|
68
|
+
}
|
|
69
|
+
async function readProvisionResponseMessage(response) {
|
|
70
|
+
const text = await response.text().catch(() => '');
|
|
71
|
+
if (!text) {
|
|
72
|
+
return undefined;
|
|
73
|
+
}
|
|
74
|
+
try {
|
|
75
|
+
const body = JSON.parse(text);
|
|
76
|
+
return typeof body.message === 'string'
|
|
77
|
+
? body.message
|
|
78
|
+
: typeof body.error === 'string'
|
|
79
|
+
? body.error
|
|
80
|
+
: text;
|
|
81
|
+
}
|
|
82
|
+
catch {
|
|
83
|
+
return text;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
23
86
|
function remapPodConflict(error, podName) {
|
|
24
87
|
const message = error instanceof Error ? error.message : String(error);
|
|
25
88
|
if (/There already is a resource at/i.test(message)) {
|
|
@@ -33,8 +96,9 @@ class ProvisionPodCreator extends community_server_1.BasePodCreator {
|
|
|
33
96
|
constructor(args) {
|
|
34
97
|
super(args);
|
|
35
98
|
this.provisionLogger = (0, global_logger_factory_1.getLoggerFor)(this);
|
|
36
|
-
this.
|
|
37
|
-
this.
|
|
99
|
+
this.oidcIssuer = normalizeOptionalUrl(args.provisionBaseUrl);
|
|
100
|
+
this.currentNodeId = normalizeOptionalString(args.nodeId);
|
|
101
|
+
this.codec = new ProvisionCodeCodec_1.ProvisionCodeCodec(this.oidcIssuer ?? args.baseUrl);
|
|
38
102
|
}
|
|
39
103
|
async handle(input) {
|
|
40
104
|
const provisionCode = input.settings?.provisionCode;
|
|
@@ -46,13 +110,25 @@ class ProvisionPodCreator extends community_server_1.BasePodCreator {
|
|
|
46
110
|
if (!payload) {
|
|
47
111
|
throw new Error('Invalid or expired provisionCode');
|
|
48
112
|
}
|
|
49
|
-
this.provisionLogger.info(`Provisioning pod on remote SP: ${payload.spUrl}`);
|
|
50
113
|
// 1. 确定 podName
|
|
51
114
|
const podName = input.name;
|
|
52
115
|
if (!podName) {
|
|
53
116
|
throw new Error('Pod name is required for remote provisioning');
|
|
54
117
|
}
|
|
55
|
-
const webId = input.webId ??
|
|
118
|
+
const webId = input.webId ?? buildDefaultWebId(this.oidcIssuer ?? this.baseUrl, podName, this.relativeWebIdPath);
|
|
119
|
+
const targetStorageRoot = buildStorageRoot(payload);
|
|
120
|
+
const canonicalStorageUrl = buildPodUrl(targetStorageRoot, podName);
|
|
121
|
+
if (this.targetsCurrentStorageProvider(payload, targetStorageRoot)) {
|
|
122
|
+
this.provisionLogger.info(`Provision code targets current SP ${this.baseUrl}${this.currentNodeId ? ` (${this.currentNodeId})` : ''}; creating Pod directly through CSS`);
|
|
123
|
+
return this.handleStandardPodCreate(input, {
|
|
124
|
+
baseIdentifier: { path: canonicalStorageUrl },
|
|
125
|
+
linkWebId: !input.webId,
|
|
126
|
+
oidcIssuer: this.oidcIssuer ?? this.baseUrl,
|
|
127
|
+
storageUrl: canonicalStorageUrl,
|
|
128
|
+
webId,
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
this.provisionLogger.info(`Provisioning pod on remote SP: ${payload.spUrl}`);
|
|
56
132
|
// 2. 回调 SP 创建 Pod
|
|
57
133
|
const callbackUrl = `${payload.spUrl.replace(/\/$/, '')}/provision/pods`;
|
|
58
134
|
const spResponse = await fetch(callbackUrl, {
|
|
@@ -64,22 +140,23 @@ class ProvisionPodCreator extends community_server_1.BasePodCreator {
|
|
|
64
140
|
body: JSON.stringify({ podName, webId }),
|
|
65
141
|
});
|
|
66
142
|
if (!spResponse.ok) {
|
|
67
|
-
const
|
|
68
|
-
this.provisionLogger.error(`SP callback failed: ${spResponse.status} ${
|
|
69
|
-
|
|
143
|
+
const message = await readProvisionResponseMessage(spResponse);
|
|
144
|
+
this.provisionLogger.error(`SP callback failed: ${spResponse.status} ${message ?? ''}`);
|
|
145
|
+
if (spResponse.status === 409 || /already exists|already taken|conflict/iu.test(message ?? '')) {
|
|
146
|
+
throw new community_server_1.ConflictHttpError(message || `Pod name "${podName}" is already taken for this storage target.`);
|
|
147
|
+
}
|
|
148
|
+
throw new Error(message
|
|
149
|
+
? `Failed to create pod on SP: ${spResponse.status}: ${message}`
|
|
150
|
+
: `Failed to create pod on SP: ${spResponse.status}`);
|
|
70
151
|
}
|
|
71
152
|
const spResult = await spResponse.json();
|
|
72
153
|
// storage URL 优先用 Cloud 分配的子域名,回调用实际地址
|
|
73
|
-
const storageBase = payload.spDomain
|
|
74
|
-
? `https://${payload.spDomain}`
|
|
75
|
-
: payload.spUrl.replace(/\/$/, '');
|
|
76
|
-
const canonicalStorageUrl = `${storageBase}/${podName}/`;
|
|
77
154
|
const podUrl = spResult.podUrl || canonicalStorageUrl;
|
|
78
155
|
// 3. 链接 WebID 到账户 + 在本地 PodStore 记录
|
|
79
156
|
// base.path 必须在 Cloud 的 identifier space 内(CSS PodStore 会检查),
|
|
80
157
|
// 所以用 Cloud 本地路径;真实的 SP storage URL 通过 podUrl 返回。
|
|
81
158
|
const localBase = this.identifierGenerator.generate(podName);
|
|
82
|
-
const
|
|
159
|
+
const inputSettings = stripProvisionCode(input.settings);
|
|
83
160
|
const podSettings = {
|
|
84
161
|
...inputSettings,
|
|
85
162
|
base: localBase,
|
|
@@ -87,37 +164,49 @@ class ProvisionPodCreator extends community_server_1.BasePodCreator {
|
|
|
87
164
|
oidcIssuer: this.baseUrl,
|
|
88
165
|
storage: canonicalStorageUrl,
|
|
89
166
|
};
|
|
90
|
-
const webIdLink = await this.
|
|
91
|
-
|
|
92
|
-
await this.
|
|
167
|
+
const webIdLink = await this.prepareWebIdLink(!input.webId, webId, input.accountId, podSettings);
|
|
168
|
+
podSettings.oidcIssuer = this.oidcIssuer ?? this.baseUrl;
|
|
169
|
+
const podId = await this.createPod(input.accountId, podSettings, !input.name, webIdLink.cleanupWebIdLink);
|
|
93
170
|
this.provisionLogger.info(`Provisioned pod ${podName} on SP ${payload.spUrl}, podUrl: ${podUrl}`);
|
|
94
171
|
return {
|
|
95
172
|
podUrl,
|
|
96
173
|
webId,
|
|
97
174
|
podId,
|
|
98
|
-
webIdLink,
|
|
175
|
+
webIdLink: webIdLink.outputWebIdLink,
|
|
99
176
|
};
|
|
100
177
|
}
|
|
101
|
-
|
|
178
|
+
targetsCurrentStorageProvider(payload, targetStorageRoot) {
|
|
179
|
+
return isSameNodeId(payload.nodeId, this.currentNodeId) ||
|
|
180
|
+
isSameUrlRoot(payload.spUrl, this.baseUrl) ||
|
|
181
|
+
isSameUrlRoot(targetStorageRoot, this.baseUrl);
|
|
182
|
+
}
|
|
183
|
+
async handleStandardPodCreate(input, options = {}) {
|
|
102
184
|
const totalStarted = Date.now();
|
|
103
|
-
const baseIdentifier = this.generateBaseIdentifier(input.name);
|
|
104
|
-
const
|
|
105
|
-
const
|
|
106
|
-
|
|
185
|
+
const baseIdentifier = options.baseIdentifier ?? this.generateBaseIdentifier(input.name);
|
|
186
|
+
const inputSettings = stripProvisionCode(input.settings);
|
|
187
|
+
const oidcIssuer = options.oidcIssuer ?? (typeof inputSettings?.oidcIssuer === 'string'
|
|
188
|
+
? inputSettings.oidcIssuer
|
|
189
|
+
: this.oidcIssuer ?? this.baseUrl);
|
|
190
|
+
const webId = options.webId ?? input.webId ?? (input.name
|
|
191
|
+
? buildDefaultWebId(oidcIssuer, input.name, this.relativeWebIdPath)
|
|
192
|
+
: joinUrlPath(baseIdentifier.path, this.relativeWebIdPath));
|
|
193
|
+
const storageUrl = options.storageUrl ?? baseIdentifier.path;
|
|
107
194
|
const podSettings = {
|
|
108
195
|
...inputSettings,
|
|
109
196
|
base: baseIdentifier,
|
|
110
197
|
webId,
|
|
111
198
|
oidcIssuer,
|
|
112
|
-
storage:
|
|
199
|
+
storage: storageUrl,
|
|
113
200
|
};
|
|
201
|
+
const linkWebId = options.linkWebId ?? !input.webId;
|
|
114
202
|
const webIdStarted = Date.now();
|
|
115
|
-
const webIdLink = await this.
|
|
203
|
+
const webIdLink = await this.prepareWebIdLink(linkWebId, webId, input.accountId, podSettings);
|
|
204
|
+
podSettings.oidcIssuer = oidcIssuer;
|
|
116
205
|
const webIdElapsed = Date.now() - webIdStarted;
|
|
117
206
|
const podStarted = Date.now();
|
|
118
207
|
let podId;
|
|
119
208
|
try {
|
|
120
|
-
podId = await this.createPod(input.accountId, podSettings, !input.name, webIdLink);
|
|
209
|
+
podId = await this.createPod(input.accountId, podSettings, !input.name, webIdLink.cleanupWebIdLink);
|
|
121
210
|
}
|
|
122
211
|
catch (error) {
|
|
123
212
|
if (input.name) {
|
|
@@ -132,9 +221,29 @@ class ProvisionPodCreator extends community_server_1.BasePodCreator {
|
|
|
132
221
|
podUrl: baseIdentifier.path,
|
|
133
222
|
webId,
|
|
134
223
|
podId,
|
|
135
|
-
webIdLink,
|
|
224
|
+
webIdLink: webIdLink.outputWebIdLink,
|
|
136
225
|
};
|
|
137
226
|
}
|
|
227
|
+
async prepareWebIdLink(linkWebId, webId, accountId, settings) {
|
|
228
|
+
if (!linkWebId) {
|
|
229
|
+
return {};
|
|
230
|
+
}
|
|
231
|
+
const existingLink = await this.findExistingWebIdLink(webId, accountId);
|
|
232
|
+
if (existingLink) {
|
|
233
|
+
this.provisionLogger.info(`Reusing existing WebID link ${existingLink.id} for ${webId}`);
|
|
234
|
+
return { outputWebIdLink: existingLink.id };
|
|
235
|
+
}
|
|
236
|
+
const createdLink = await this.handleWebId(true, webId, accountId, settings);
|
|
237
|
+
return {
|
|
238
|
+
outputWebIdLink: createdLink,
|
|
239
|
+
cleanupWebIdLink: createdLink,
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
async findExistingWebIdLink(webId, accountId) {
|
|
243
|
+
const normalizedTarget = normalizeUrlRoot(webId) ?? webId;
|
|
244
|
+
const links = await this.webIdStore.findLinks(accountId);
|
|
245
|
+
return links.find((link) => (normalizeUrlRoot(link.webId) ?? link.webId) === normalizedTarget);
|
|
246
|
+
}
|
|
138
247
|
}
|
|
139
248
|
exports.ProvisionPodCreator = ProvisionPodCreator;
|
|
140
249
|
//# sourceMappingURL=ProvisionPodCreator.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ProvisionPodCreator.js","sourceRoot":"","sources":["../../src/provision/ProvisionPodCreator.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * ProvisionPodCreator\n *\n * 等位替换 CSS 的 BasePodCreator。\n *\n * 检查 settings 里有没有 provisionCode:\n * - 有 → 解码 JWT,回调远端 SP 的 /provision/pods 创建 Pod\n * - 没有 → 委托给原始 BasePodCreator(标准本地创建)\n */\n\nimport { getLoggerFor } from 'global-logger-factory';\nimport {\n BasePodCreator,\n type PodCreatorInput,\n type PodCreatorOutput,\n type BasePodCreatorArgs,\n ConflictHttpError,\n} from '@solid/community-server';\nimport { ProvisionCodeCodec } from './ProvisionCodeCodec';\nimport { PodLookupRepository } from '../identity/drizzle/PodLookupRepository';\nimport { getIdentityDatabase } from '../identity/drizzle/db';\n\nfunction joinUrlPath(baseUrl: string, relativePath: string): string {\n const normalizedBaseUrl = baseUrl.replace(/\\/+$/u, '');\n const normalizedRelativePath = relativePath.replace(/^\\/+/u, '');\n return `${normalizedBaseUrl}/${normalizedRelativePath}`;\n}\n\nexport interface ProvisionPodCreatorArgs extends BasePodCreatorArgs {\n /** 与 ProvisionHandler 使用相同的 baseUrl 派生签名密钥 */\n provisionBaseUrl?: string;\n /** Optional identity database connection string used to persist Pod-side storage facts. */\n identityDbUrl?: string;\n}\n\nfunction remapPodConflict(error: unknown, podName: string): never {\n const message = error instanceof Error ? error.message : String(error);\n if (/There already is a resource at/i.test(message)) {\n throw new ConflictHttpError(`Pod name \"${podName}\" is already taken for this storage target.`, {\n cause: error instanceof Error ? error : undefined,\n });\n }\n\n throw error;\n}\n\nexport class ProvisionPodCreator extends BasePodCreator {\n private readonly provisionLogger = getLoggerFor(this);\n private readonly codec: ProvisionCodeCodec;\n private readonly podLookupRepo?: PodLookupRepository;\n\n public constructor(args: ProvisionPodCreatorArgs) {\n super(args);\n this.codec = new ProvisionCodeCodec(args.provisionBaseUrl ?? args.baseUrl);\n this.podLookupRepo = args.identityDbUrl ? new PodLookupRepository(getIdentityDatabase(args.identityDbUrl)) : undefined;\n }\n\n public override async handle(input: PodCreatorInput): Promise<PodCreatorOutput> {\n const provisionCode = input.settings?.provisionCode as string | undefined;\n\n if (!provisionCode) {\n return this.handleStandardPodCreate(input);\n }\n\n // SP 模式:解码 provisionCode,回调远端 SP\n const payload = this.codec.decode(provisionCode);\n if (!payload) {\n throw new Error('Invalid or expired provisionCode');\n }\n\n this.provisionLogger.info(`Provisioning pod on remote SP: ${payload.spUrl}`);\n\n // 1. 确定 podName\n const podName = input.name;\n if (!podName) {\n throw new Error('Pod name is required for remote provisioning');\n }\n const webId = input.webId ?? `${this.baseUrl}${podName}/profile/card#me`;\n\n // 2. 回调 SP 创建 Pod\n const callbackUrl = `${payload.spUrl.replace(/\\/$/, '')}/provision/pods`;\n const spResponse = await fetch(callbackUrl, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${payload.serviceToken}`,\n },\n body: JSON.stringify({ podName, webId }),\n });\n\n if (!spResponse.ok) {\n const errBody = await spResponse.text();\n this.provisionLogger.error(`SP callback failed: ${spResponse.status} ${errBody}`);\n throw new Error(`Failed to create pod on SP: ${spResponse.status}`);\n }\n\n const spResult = await spResponse.json() as { podUrl?: string };\n\n // storage URL 优先用 Cloud 分配的子域名,回调用实际地址\n const storageBase = payload.spDomain\n ? `https://${payload.spDomain}`\n : payload.spUrl.replace(/\\/$/, '');\n const canonicalStorageUrl = `${storageBase}/${podName}/`;\n const podUrl = spResult.podUrl || canonicalStorageUrl;\n\n // 3. 链接 WebID 到账户 + 在本地 PodStore 记录\n // base.path 必须在 Cloud 的 identifier space 内(CSS PodStore 会检查),\n // 所以用 Cloud 本地路径;真实的 SP storage URL 通过 podUrl 返回。\n const localBase = this.identifierGenerator.generate(podName);\n const { provisionCode: _provisionCode, ...inputSettings } = input.settings ?? {};\n const podSettings = {\n ...inputSettings,\n base: localBase,\n webId,\n oidcIssuer: this.baseUrl,\n storage: canonicalStorageUrl,\n };\n\n const webIdLink = await this.handleWebId(!input.webId, webId, input.accountId, podSettings);\n const podId = await this.createPod(input.accountId, podSettings, !input.name, webIdLink);\n await this.podLookupRepo?.setStorageUrl(podId, input.accountId, canonicalStorageUrl);\n\n this.provisionLogger.info(`Provisioned pod ${podName} on SP ${payload.spUrl}, podUrl: ${podUrl}`);\n\n return {\n podUrl,\n webId,\n podId,\n webIdLink,\n };\n }\n\n private async handleStandardPodCreate(input: PodCreatorInput): Promise<PodCreatorOutput> {\n const totalStarted = Date.now();\n const baseIdentifier = this.generateBaseIdentifier(input.name);\n const webId = input.webId ?? joinUrlPath(baseIdentifier.path, this.relativeWebIdPath);\n const inputSettings = input.settings as Record<string, unknown> | undefined;\n const oidcIssuer = typeof inputSettings?.oidcIssuer === 'string' ? inputSettings.oidcIssuer : this.baseUrl;\n const podSettings = {\n ...inputSettings,\n base: baseIdentifier,\n webId,\n oidcIssuer,\n storage: baseIdentifier.path,\n };\n\n const webIdStarted = Date.now();\n const webIdLink = await this.handleWebId(!input.webId, webId, input.accountId, podSettings);\n const webIdElapsed = Date.now() - webIdStarted;\n\n const podStarted = Date.now();\n let podId: string;\n try {\n podId = await this.createPod(input.accountId, podSettings, !input.name, webIdLink);\n } catch (error) {\n if (input.name) {\n remapPodConflict(error, input.name);\n }\n throw error;\n }\n const podElapsed = Date.now() - podStarted;\n const totalElapsed = Date.now() - totalStarted;\n\n this.provisionLogger.info(\n `[timing] ProvisionPodCreator.standard account=${input.accountId} pod=${baseIdentifier.path} handleWebId=${webIdElapsed}ms createPod=${podElapsed}ms total=${totalElapsed}ms`,\n );\n\n return {\n podUrl: baseIdentifier.path,\n webId,\n podId,\n webIdLink,\n };\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"ProvisionPodCreator.js","sourceRoot":"","sources":["../../src/provision/ProvisionPodCreator.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * ProvisionPodCreator\n *\n * 等位替换 CSS 的 BasePodCreator。\n *\n * 检查 settings 里有没有 provisionCode:\n * - 有 → 解码 JWT,回调远端 SP 的 /provision/pods 创建 Pod\n * - 没有 → 委托给原始 BasePodCreator(标准本地创建)\n */\n\nimport { getLoggerFor } from 'global-logger-factory';\nimport {\n BasePodCreator,\n type PodCreatorInput,\n type PodCreatorOutput,\n type BasePodCreatorArgs,\n type ResourceIdentifier,\n type PodSettings,\n ConflictHttpError,\n} from '@solid/community-server';\nimport { ProvisionCodeCodec } from './ProvisionCodeCodec';\n\nfunction joinUrlPath(baseUrl: string, relativePath: string): string {\n const normalizedBaseUrl = baseUrl.replace(/\\/+$/u, '');\n const normalizedRelativePath = relativePath.replace(/^\\/+/u, '');\n return `${normalizedBaseUrl}/${normalizedRelativePath}`;\n}\n\nfunction normalizeOptionalUrl(url: string | undefined): string | undefined {\n const trimmed = url?.trim();\n return trimmed ? trimmed : undefined;\n}\n\nfunction normalizeOptionalString(value: string | undefined): string | undefined {\n const trimmed = value?.trim();\n return trimmed ? trimmed : undefined;\n}\n\nfunction normalizeUrlRoot(url: string | undefined): string | undefined {\n if (!url) {\n return undefined;\n }\n\n try {\n const parsed = new URL(url);\n parsed.pathname = parsed.pathname.replace(/\\/+$/u, '') || '/';\n parsed.search = '';\n parsed.hash = '';\n return parsed.toString();\n } catch {\n return undefined;\n }\n}\n\nfunction isSameUrlRoot(left: string | undefined, right: string | undefined): boolean {\n const normalizedLeft = normalizeUrlRoot(left);\n const normalizedRight = normalizeUrlRoot(right);\n return Boolean(normalizedLeft && normalizedRight && normalizedLeft === normalizedRight);\n}\n\nfunction isSameNodeId(left: string | undefined, right: string | undefined): boolean {\n return Boolean(left && right && left.trim() === right.trim());\n}\n\nfunction buildDefaultWebId(issuer: string, podName: string, relativeWebIdPath: string): string {\n const normalizedRelativePath = relativeWebIdPath.replace(/^\\/+/u, '');\n return joinUrlPath(issuer, `${encodeURIComponent(podName)}/${normalizedRelativePath}`);\n}\n\nfunction buildStorageRoot(payload: { spDomain?: string; spUrl: string }): string {\n return payload.spDomain ? `https://${payload.spDomain}` : payload.spUrl;\n}\n\nfunction buildPodUrl(storageRoot: string, podName: string): string {\n return joinUrlPath(storageRoot, `${encodeURIComponent(podName)}/`);\n}\n\nfunction stripProvisionCode(settings: PodCreatorInput['settings']): Record<string, unknown> | undefined {\n if (!settings) {\n return undefined;\n }\n\n const { provisionCode: _provisionCode, ...rest } = settings as Record<string, unknown>;\n return rest;\n}\n\nasync function readProvisionResponseMessage(response: Response): Promise<string | undefined> {\n const text = await response.text().catch(() => '');\n if (!text) {\n return undefined;\n }\n\n try {\n const body = JSON.parse(text) as { message?: unknown; error?: unknown };\n return typeof body.message === 'string'\n ? body.message\n : typeof body.error === 'string'\n ? body.error\n : text;\n } catch {\n return text;\n }\n}\n\nexport interface ProvisionPodCreatorArgs extends BasePodCreatorArgs {\n /** 与 ProvisionHandler 使用相同的 baseUrl 派生签名密钥 */\n provisionBaseUrl?: string;\n /** Current SP node id; used to recognize this SP even when URLs differ by localhost/managed domain. */\n nodeId?: string;\n /** Kept in the component signature for config compatibility; Pod storage facts live in CSS account data. */\n identityDbUrl?: string;\n}\n\ninterface StandardPodCreateOptions {\n baseIdentifier?: ResourceIdentifier;\n linkWebId?: boolean;\n oidcIssuer?: string;\n storageUrl?: string;\n webId?: string;\n}\n\ninterface PreparedWebIdLink {\n /** Link id to expose in the create-pod response. May be an existing link. */\n outputWebIdLink?: string;\n /** Link id CSS may delete if Pod creation fails. Only newly-created links are safe here. */\n cleanupWebIdLink?: string;\n}\n\nfunction remapPodConflict(error: unknown, podName: string): never {\n const message = error instanceof Error ? error.message : String(error);\n if (/There already is a resource at/i.test(message)) {\n throw new ConflictHttpError(`Pod name \"${podName}\" is already taken for this storage target.`, {\n cause: error instanceof Error ? error : undefined,\n });\n }\n\n throw error;\n}\n\nexport class ProvisionPodCreator extends BasePodCreator {\n private readonly provisionLogger = getLoggerFor(this);\n private readonly codec: ProvisionCodeCodec;\n private readonly oidcIssuer?: string;\n private readonly currentNodeId?: string;\n\n public constructor(args: ProvisionPodCreatorArgs) {\n super(args);\n this.oidcIssuer = normalizeOptionalUrl(args.provisionBaseUrl);\n this.currentNodeId = normalizeOptionalString(args.nodeId);\n this.codec = new ProvisionCodeCodec(this.oidcIssuer ?? args.baseUrl);\n }\n\n public override async handle(input: PodCreatorInput): Promise<PodCreatorOutput> {\n const provisionCode = input.settings?.provisionCode as string | undefined;\n\n if (!provisionCode) {\n return this.handleStandardPodCreate(input);\n }\n\n // SP 模式:解码 provisionCode,回调远端 SP\n const payload = this.codec.decode(provisionCode);\n if (!payload) {\n throw new Error('Invalid or expired provisionCode');\n }\n\n // 1. 确定 podName\n const podName = input.name;\n if (!podName) {\n throw new Error('Pod name is required for remote provisioning');\n }\n const webId = input.webId ?? buildDefaultWebId(this.oidcIssuer ?? this.baseUrl, podName, this.relativeWebIdPath);\n const targetStorageRoot = buildStorageRoot(payload);\n const canonicalStorageUrl = buildPodUrl(targetStorageRoot, podName);\n\n if (this.targetsCurrentStorageProvider(payload, targetStorageRoot)) {\n this.provisionLogger.info(\n `Provision code targets current SP ${this.baseUrl}${this.currentNodeId ? ` (${this.currentNodeId})` : ''}; creating Pod directly through CSS`,\n );\n return this.handleStandardPodCreate(input, {\n baseIdentifier: { path: canonicalStorageUrl },\n linkWebId: !input.webId,\n oidcIssuer: this.oidcIssuer ?? this.baseUrl,\n storageUrl: canonicalStorageUrl,\n webId,\n });\n }\n\n this.provisionLogger.info(`Provisioning pod on remote SP: ${payload.spUrl}`);\n\n // 2. 回调 SP 创建 Pod\n const callbackUrl = `${payload.spUrl.replace(/\\/$/, '')}/provision/pods`;\n const spResponse = await fetch(callbackUrl, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${payload.serviceToken}`,\n },\n body: JSON.stringify({ podName, webId }),\n });\n\n if (!spResponse.ok) {\n const message = await readProvisionResponseMessage(spResponse);\n this.provisionLogger.error(`SP callback failed: ${spResponse.status} ${message ?? ''}`);\n if (spResponse.status === 409 || /already exists|already taken|conflict/iu.test(message ?? '')) {\n throw new ConflictHttpError(message || `Pod name \"${podName}\" is already taken for this storage target.`);\n }\n throw new Error(message\n ? `Failed to create pod on SP: ${spResponse.status}: ${message}`\n : `Failed to create pod on SP: ${spResponse.status}`);\n }\n\n const spResult = await spResponse.json() as { podUrl?: string };\n\n // storage URL 优先用 Cloud 分配的子域名,回调用实际地址\n const podUrl = spResult.podUrl || canonicalStorageUrl;\n\n // 3. 链接 WebID 到账户 + 在本地 PodStore 记录\n // base.path 必须在 Cloud 的 identifier space 内(CSS PodStore 会检查),\n // 所以用 Cloud 本地路径;真实的 SP storage URL 通过 podUrl 返回。\n const localBase = this.identifierGenerator.generate(podName);\n const inputSettings = stripProvisionCode(input.settings);\n const podSettings = {\n ...inputSettings,\n base: localBase,\n webId,\n oidcIssuer: this.baseUrl,\n storage: canonicalStorageUrl,\n };\n\n const webIdLink = await this.prepareWebIdLink(!input.webId, webId, input.accountId, podSettings);\n podSettings.oidcIssuer = this.oidcIssuer ?? this.baseUrl;\n const podId = await this.createPod(input.accountId, podSettings, !input.name, webIdLink.cleanupWebIdLink);\n\n this.provisionLogger.info(`Provisioned pod ${podName} on SP ${payload.spUrl}, podUrl: ${podUrl}`);\n\n return {\n podUrl,\n webId,\n podId,\n webIdLink: webIdLink.outputWebIdLink,\n };\n }\n\n private targetsCurrentStorageProvider(payload: { nodeId?: string; spUrl: string }, targetStorageRoot: string): boolean {\n return isSameNodeId(payload.nodeId, this.currentNodeId) ||\n isSameUrlRoot(payload.spUrl, this.baseUrl) ||\n isSameUrlRoot(targetStorageRoot, this.baseUrl);\n }\n\n private async handleStandardPodCreate(\n input: PodCreatorInput,\n options: StandardPodCreateOptions = {},\n ): Promise<PodCreatorOutput> {\n const totalStarted = Date.now();\n const baseIdentifier = options.baseIdentifier ?? this.generateBaseIdentifier(input.name);\n const inputSettings = stripProvisionCode(input.settings);\n const oidcIssuer = options.oidcIssuer ?? (typeof inputSettings?.oidcIssuer === 'string'\n ? inputSettings.oidcIssuer\n : this.oidcIssuer ?? this.baseUrl);\n const webId = options.webId ?? input.webId ?? (input.name\n ? buildDefaultWebId(oidcIssuer, input.name, this.relativeWebIdPath)\n : joinUrlPath(baseIdentifier.path, this.relativeWebIdPath));\n const storageUrl = options.storageUrl ?? baseIdentifier.path;\n const podSettings = {\n ...inputSettings,\n base: baseIdentifier,\n webId,\n oidcIssuer,\n storage: storageUrl,\n };\n const linkWebId = options.linkWebId ?? !input.webId;\n\n const webIdStarted = Date.now();\n const webIdLink = await this.prepareWebIdLink(linkWebId, webId, input.accountId, podSettings);\n podSettings.oidcIssuer = oidcIssuer;\n const webIdElapsed = Date.now() - webIdStarted;\n\n const podStarted = Date.now();\n let podId: string;\n try {\n podId = await this.createPod(input.accountId, podSettings, !input.name, webIdLink.cleanupWebIdLink);\n } catch (error) {\n if (input.name) {\n remapPodConflict(error, input.name);\n }\n throw error;\n }\n const podElapsed = Date.now() - podStarted;\n const totalElapsed = Date.now() - totalStarted;\n\n this.provisionLogger.info(\n `[timing] ProvisionPodCreator.standard account=${input.accountId} pod=${baseIdentifier.path} handleWebId=${webIdElapsed}ms createPod=${podElapsed}ms total=${totalElapsed}ms`,\n );\n\n return {\n podUrl: baseIdentifier.path,\n webId,\n podId,\n webIdLink: webIdLink.outputWebIdLink,\n };\n }\n\n private async prepareWebIdLink(\n linkWebId: boolean,\n webId: string,\n accountId: string,\n settings: PodSettings,\n ): Promise<PreparedWebIdLink> {\n if (!linkWebId) {\n return {};\n }\n\n const existingLink = await this.findExistingWebIdLink(webId, accountId);\n if (existingLink) {\n this.provisionLogger.info(`Reusing existing WebID link ${existingLink.id} for ${webId}`);\n return { outputWebIdLink: existingLink.id };\n }\n\n const createdLink = await this.handleWebId(true, webId, accountId, settings);\n return {\n outputWebIdLink: createdLink,\n cleanupWebIdLink: createdLink,\n };\n }\n\n private async findExistingWebIdLink(\n webId: string,\n accountId: string,\n ): Promise<{ id: string; webId: string } | undefined> {\n const normalizedTarget = normalizeUrlRoot(webId) ?? webId;\n const links = await this.webIdStore.findLinks(accountId);\n return links.find((link) => (normalizeUrlRoot(link.webId) ?? link.webId) === normalizedTarget);\n }\n}\n"]}
|
|
@@ -26,6 +26,19 @@
|
|
|
26
26
|
},
|
|
27
27
|
"comment": "与 ProvisionHandler 使用相同的 baseUrl 派生签名密钥"
|
|
28
28
|
},
|
|
29
|
+
{
|
|
30
|
+
"@id": "undefineds:dist/provision/ProvisionPodCreator.jsonld#ProvisionPodCreator_args_nodeId",
|
|
31
|
+
"range": {
|
|
32
|
+
"@type": "ParameterRangeUnion",
|
|
33
|
+
"parameterRangeElements": [
|
|
34
|
+
"xsd:string",
|
|
35
|
+
{
|
|
36
|
+
"@type": "ParameterRangeUndefined"
|
|
37
|
+
}
|
|
38
|
+
]
|
|
39
|
+
},
|
|
40
|
+
"comment": "Current SP node id; used to recognize this SP even when URLs differ by localhost/managed domain."
|
|
41
|
+
},
|
|
29
42
|
{
|
|
30
43
|
"@id": "undefineds:dist/provision/ProvisionPodCreator.jsonld#ProvisionPodCreator_args_identityDbUrl",
|
|
31
44
|
"range": {
|
|
@@ -37,7 +50,7 @@
|
|
|
37
50
|
}
|
|
38
51
|
]
|
|
39
52
|
},
|
|
40
|
-
"comment": "
|
|
53
|
+
"comment": "Kept in the component signature for config compatibility; Pod storage facts live in CSS account data."
|
|
41
54
|
},
|
|
42
55
|
{
|
|
43
56
|
"@id": "undefineds:dist/provision/ProvisionPodCreator.jsonld#ProvisionPodCreator_args_baseUrl",
|
|
@@ -75,8 +88,12 @@
|
|
|
75
88
|
"memberFieldName": "codec"
|
|
76
89
|
},
|
|
77
90
|
{
|
|
78
|
-
"@id": "undefineds:dist/provision/ProvisionPodCreator.jsonld#
|
|
79
|
-
"memberFieldName": "
|
|
91
|
+
"@id": "undefineds:dist/provision/ProvisionPodCreator.jsonld#ProvisionPodCreator__member_oidcIssuer",
|
|
92
|
+
"memberFieldName": "oidcIssuer"
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
"@id": "undefineds:dist/provision/ProvisionPodCreator.jsonld#ProvisionPodCreator__member_currentNodeId",
|
|
96
|
+
"memberFieldName": "currentNodeId"
|
|
80
97
|
},
|
|
81
98
|
{
|
|
82
99
|
"@id": "undefineds:dist/provision/ProvisionPodCreator.jsonld#ProvisionPodCreator__member_constructor",
|
|
@@ -86,9 +103,21 @@
|
|
|
86
103
|
"@id": "undefineds:dist/provision/ProvisionPodCreator.jsonld#ProvisionPodCreator__member_handle",
|
|
87
104
|
"memberFieldName": "handle"
|
|
88
105
|
},
|
|
106
|
+
{
|
|
107
|
+
"@id": "undefineds:dist/provision/ProvisionPodCreator.jsonld#ProvisionPodCreator__member_targetsCurrentStorageProvider",
|
|
108
|
+
"memberFieldName": "targetsCurrentStorageProvider"
|
|
109
|
+
},
|
|
89
110
|
{
|
|
90
111
|
"@id": "undefineds:dist/provision/ProvisionPodCreator.jsonld#ProvisionPodCreator__member_handleStandardPodCreate",
|
|
91
112
|
"memberFieldName": "handleStandardPodCreate"
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
"@id": "undefineds:dist/provision/ProvisionPodCreator.jsonld#ProvisionPodCreator__member_prepareWebIdLink",
|
|
116
|
+
"memberFieldName": "prepareWebIdLink"
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
"@id": "undefineds:dist/provision/ProvisionPodCreator.jsonld#ProvisionPodCreator__member_findExistingWebIdLink",
|
|
120
|
+
"memberFieldName": "findExistingWebIdLink"
|
|
92
121
|
}
|
|
93
122
|
],
|
|
94
123
|
"constructorArguments": [
|
|
@@ -101,6 +130,12 @@
|
|
|
101
130
|
"@id": "undefineds:dist/provision/ProvisionPodCreator.jsonld#ProvisionPodCreator_args_provisionBaseUrl"
|
|
102
131
|
}
|
|
103
132
|
},
|
|
133
|
+
{
|
|
134
|
+
"keyRaw": "nodeId",
|
|
135
|
+
"value": {
|
|
136
|
+
"@id": "undefineds:dist/provision/ProvisionPodCreator.jsonld#ProvisionPodCreator_args_nodeId"
|
|
137
|
+
}
|
|
138
|
+
},
|
|
104
139
|
{
|
|
105
140
|
"keyRaw": "identityDbUrl",
|
|
106
141
|
"value": {
|
|
@@ -21,10 +21,6 @@ export declare class DrizzleQuotaService implements QuotaService {
|
|
|
21
21
|
setPodQuota(podId: string, quota: Partial<AccountQuota>): Promise<void>;
|
|
22
22
|
clearAccountQuota(accountId: string): Promise<void>;
|
|
23
23
|
clearPodQuota(podId: string): Promise<void>;
|
|
24
|
-
getAccountLimit(accountId: string): Promise<number | null | undefined>;
|
|
25
|
-
getPodLimit(podId: string): Promise<number | null | undefined>;
|
|
26
|
-
setAccountLimit(accountId: string, limit: number | null): Promise<void>;
|
|
27
|
-
setPodLimit(podId: string, limit: number | null): Promise<void>;
|
|
28
24
|
/**
|
|
29
25
|
* 解析配额值:本地自定义配额 > 远端 entitlement > 环境变量默认值 > null (不限制)
|
|
30
26
|
*/
|
|
@@ -74,9 +74,8 @@ class DrizzleQuotaService {
|
|
|
74
74
|
const usage = await this.usageRepo.getPodUsage(podId);
|
|
75
75
|
accountId = usage?.accountId;
|
|
76
76
|
}
|
|
77
|
-
// If still no accountId, use a placeholder (quota can be set before pod creation)
|
|
78
77
|
if (!accountId) {
|
|
79
|
-
|
|
78
|
+
throw new Error(`Pod ${podId} not found or has no associated account`);
|
|
80
79
|
}
|
|
81
80
|
if (quota.storageLimitBytes !== undefined) {
|
|
82
81
|
await this.usageRepo.setPodStorageLimit(podId, accountId, quota.storageLimitBytes);
|
|
@@ -107,25 +106,6 @@ class DrizzleQuotaService {
|
|
|
107
106
|
tokenLimitMonthly: null,
|
|
108
107
|
});
|
|
109
108
|
}
|
|
110
|
-
// 向后兼容
|
|
111
|
-
async getAccountLimit(accountId) {
|
|
112
|
-
const quota = await this.getAccountQuota(accountId);
|
|
113
|
-
return quota.storageLimitBytes;
|
|
114
|
-
}
|
|
115
|
-
async getPodLimit(podId) {
|
|
116
|
-
const quota = await this.getPodQuota(podId);
|
|
117
|
-
return quota.storageLimitBytes;
|
|
118
|
-
}
|
|
119
|
-
async setAccountLimit(accountId, limit) {
|
|
120
|
-
await this.usageRepo.setAccountStorageLimit(accountId, limit);
|
|
121
|
-
}
|
|
122
|
-
async setPodLimit(podId, limit) {
|
|
123
|
-
const podInfo = await this.accountRepo.getPodInfo(podId);
|
|
124
|
-
if (!podInfo?.accountId) {
|
|
125
|
-
throw new Error(`Pod ${podId} not found or has no associated account`);
|
|
126
|
-
}
|
|
127
|
-
await this.usageRepo.setPodStorageLimit(podId, podInfo.accountId, limit);
|
|
128
|
-
}
|
|
129
109
|
/**
|
|
130
110
|
* 解析配额值:本地自定义配额 > 远端 entitlement > 环境变量默认值 > null (不限制)
|
|
131
111
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DrizzleQuotaService.js","sourceRoot":"","sources":["../../src/quota/DrizzleQuotaService.ts"],"names":[],"mappings":"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","sourcesContent":["import { getLoggerFor } from 'global-logger-factory';\nimport { getIdentityDatabase } from '../identity/drizzle/db';\nimport { AccountRepository } from '../identity/drizzle/AccountRepository';\nimport { UsageRepository } from '../storage/quota/UsageRepository';\nimport type { QuotaService, AccountQuota } from './QuotaService';\nimport type { EntitlementProvider } from './EntitlementProvider';\nimport { createEntitlementProviderFromEnv } from './EntitlementProvider';\n\n/**\n * 环境变量默认值\n */\nfunction envNumber(key: string): number | null {\n const val = process.env[key];\n if (val === undefined || val === '') {\n return null;\n }\n const num = Number(val);\n return Number.isFinite(num) && num >= 0 ? num : null;\n}\n\ninterface DrizzleQuotaServiceOptions {\n identityDbUrl: string;\n entitlementProvider?: EntitlementProvider;\n}\n\n/**\n * 统一 QuotaService 实现\n *\n * 查询优先级:本地自定义配额 > 远端 entitlement > 环境变量默认值 > 不限制 (null)\n */\nexport class DrizzleQuotaService implements QuotaService {\n private readonly logger = getLoggerFor(this);\n private readonly accountRepo: AccountRepository;\n private readonly usageRepo: UsageRepository;\n private readonly entitlementProvider: EntitlementProvider;\n\n public constructor(options: DrizzleQuotaServiceOptions) {\n const db = getIdentityDatabase(options.identityDbUrl);\n this.accountRepo = new AccountRepository(db);\n this.usageRepo = new UsageRepository(db);\n this.entitlementProvider = options.entitlementProvider ?? createEntitlementProviderFromEnv();\n }\n\n public async getAccountQuota(accountId: string): Promise<AccountQuota> {\n const usage = await this.usageRepo.getAccountUsage(accountId);\n const entitlement = this.needsRemoteEntitlement(usage)\n ? await this.entitlementProvider.getAccountEntitlement(accountId)\n : undefined;\n\n return {\n storageLimitBytes: this.resolve(usage?.storageLimitBytes, entitlement?.quota.storageLimitBytes, 'XPOD_DEFAULT_STORAGE_LIMIT_BYTES'),\n bandwidthLimitBps: this.resolve(usage?.bandwidthLimitBps, entitlement?.quota.bandwidthLimitBps, 'XPOD_DEFAULT_BANDWIDTH_LIMIT_BPS'),\n computeLimitSeconds: this.resolve(usage?.computeLimitSeconds, entitlement?.quota.computeLimitSeconds, 'XPOD_DEFAULT_COMPUTE_LIMIT_SECONDS'),\n tokenLimitMonthly: this.resolve(usage?.tokenLimitMonthly, entitlement?.quota.tokenLimitMonthly, 'XPOD_DEFAULT_TOKEN_LIMIT_MONTHLY'),\n };\n }\n\n public async setAccountQuota(accountId: string, quota: Partial<AccountQuota>): Promise<void> {\n if (quota.storageLimitBytes !== undefined) {\n await this.usageRepo.setAccountStorageLimit(accountId, quota.storageLimitBytes);\n }\n if (quota.bandwidthLimitBps !== undefined) {\n await this.usageRepo.setAccountBandwidthLimit(accountId, quota.bandwidthLimitBps);\n }\n if (quota.computeLimitSeconds !== undefined) {\n await this.usageRepo.setAccountComputeLimit(accountId, quota.computeLimitSeconds);\n }\n if (quota.tokenLimitMonthly !== undefined) {\n await this.usageRepo.setAccountTokenLimit(accountId, quota.tokenLimitMonthly);\n }\n }\n\n public async getPodQuota(podId: string): Promise<AccountQuota> {\n const usage = await this.usageRepo.getPodUsage(podId);\n return {\n storageLimitBytes: this.resolve(usage?.storageLimitBytes, undefined, 'XPOD_DEFAULT_STORAGE_LIMIT_BYTES'),\n bandwidthLimitBps: this.resolve(usage?.bandwidthLimitBps, undefined, 'XPOD_DEFAULT_BANDWIDTH_LIMIT_BPS'),\n computeLimitSeconds: this.resolve(usage?.computeLimitSeconds, undefined, 'XPOD_DEFAULT_COMPUTE_LIMIT_SECONDS'),\n tokenLimitMonthly: this.resolve(usage?.tokenLimitMonthly, undefined, 'XPOD_DEFAULT_TOKEN_LIMIT_MONTHLY'),\n };\n }\n\n public async setPodQuota(podId: string, quota: Partial<AccountQuota>): Promise<void> {\n // Try to get pod info from CSS\n const podInfo = await this.accountRepo.getPodInfo(podId);\n\n // If pod doesn't exist in CSS, try to get accountId from usage table\n let accountId = podInfo?.accountId;\n if (!accountId) {\n const usage = await this.usageRepo.getPodUsage(podId);\n accountId = usage?.accountId;\n }\n\n // If still no accountId, use a placeholder (quota can be set before pod creation)\n if (!accountId) {\n accountId = 'unknown';\n }\n\n if (quota.storageLimitBytes !== undefined) {\n await this.usageRepo.setPodStorageLimit(podId, accountId, quota.storageLimitBytes);\n }\n if (quota.bandwidthLimitBps !== undefined) {\n await this.usageRepo.setPodBandwidthLimit(podId, accountId, quota.bandwidthLimitBps);\n }\n if (quota.computeLimitSeconds !== undefined) {\n await this.usageRepo.setPodComputeLimit(podId, accountId, quota.computeLimitSeconds);\n }\n if (quota.tokenLimitMonthly !== undefined) {\n await this.usageRepo.setPodTokenLimit(podId, accountId, quota.tokenLimitMonthly);\n }\n }\n\n public async clearAccountQuota(accountId: string): Promise<void> {\n await this.setAccountQuota(accountId, {\n storageLimitBytes: null,\n bandwidthLimitBps: null,\n computeLimitSeconds: null,\n tokenLimitMonthly: null,\n });\n }\n\n public async clearPodQuota(podId: string): Promise<void> {\n await this.setPodQuota(podId, {\n storageLimitBytes: null,\n bandwidthLimitBps: null,\n computeLimitSeconds: null,\n tokenLimitMonthly: null,\n });\n }\n\n // 向后兼容\n public async getAccountLimit(accountId: string): Promise<number | null | undefined> {\n const quota = await this.getAccountQuota(accountId);\n return quota.storageLimitBytes;\n }\n\n public async getPodLimit(podId: string): Promise<number | null | undefined> {\n const quota = await this.getPodQuota(podId);\n return quota.storageLimitBytes;\n }\n\n public async setAccountLimit(accountId: string, limit: number | null): Promise<void> {\n await this.usageRepo.setAccountStorageLimit(accountId, limit);\n }\n\n public async setPodLimit(podId: string, limit: number | null): Promise<void> {\n const podInfo = await this.accountRepo.getPodInfo(podId);\n if (!podInfo?.accountId) {\n throw new Error(`Pod ${podId} not found or has no associated account`);\n }\n await this.usageRepo.setPodStorageLimit(podId, podInfo.accountId, limit);\n }\n\n /**\n * 解析配额值:本地自定义配额 > 远端 entitlement > 环境变量默认值 > null (不限制)\n */\n private resolve(dbValue: number | null | undefined, remoteValue: number | null | undefined, envKey: string): number | null {\n // DB 中有明确值(包括 0)\n if (typeof dbValue === 'number') {\n return dbValue;\n }\n if (remoteValue === null || typeof remoteValue === 'number') {\n return remoteValue;\n }\n // 回退到环境变量\n return envNumber(envKey);\n }\n\n private needsRemoteEntitlement(usage: {\n storageLimitBytes?: number | null;\n bandwidthLimitBps?: number | null;\n computeLimitSeconds?: number | null;\n tokenLimitMonthly?: number | null;\n } | undefined): boolean {\n return ![\n usage?.storageLimitBytes,\n usage?.bandwidthLimitBps,\n usage?.computeLimitSeconds,\n usage?.tokenLimitMonthly,\n ].every((value) => typeof value === 'number');\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"DrizzleQuotaService.js","sourceRoot":"","sources":["../../src/quota/DrizzleQuotaService.ts"],"names":[],"mappings":"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","sourcesContent":["import { getLoggerFor } from 'global-logger-factory';\nimport { getIdentityDatabase } from '../identity/drizzle/db';\nimport { AccountRepository } from '../identity/drizzle/AccountRepository';\nimport { UsageRepository } from '../storage/quota/UsageRepository';\nimport type { QuotaService, AccountQuota } from './QuotaService';\nimport type { EntitlementProvider } from './EntitlementProvider';\nimport { createEntitlementProviderFromEnv } from './EntitlementProvider';\n\n/**\n * 环境变量默认值\n */\nfunction envNumber(key: string): number | null {\n const val = process.env[key];\n if (val === undefined || val === '') {\n return null;\n }\n const num = Number(val);\n return Number.isFinite(num) && num >= 0 ? num : null;\n}\n\ninterface DrizzleQuotaServiceOptions {\n identityDbUrl: string;\n entitlementProvider?: EntitlementProvider;\n}\n\n/**\n * 统一 QuotaService 实现\n *\n * 查询优先级:本地自定义配额 > 远端 entitlement > 环境变量默认值 > 不限制 (null)\n */\nexport class DrizzleQuotaService implements QuotaService {\n private readonly logger = getLoggerFor(this);\n private readonly accountRepo: AccountRepository;\n private readonly usageRepo: UsageRepository;\n private readonly entitlementProvider: EntitlementProvider;\n\n public constructor(options: DrizzleQuotaServiceOptions) {\n const db = getIdentityDatabase(options.identityDbUrl);\n this.accountRepo = new AccountRepository(db);\n this.usageRepo = new UsageRepository(db);\n this.entitlementProvider = options.entitlementProvider ?? createEntitlementProviderFromEnv();\n }\n\n public async getAccountQuota(accountId: string): Promise<AccountQuota> {\n const usage = await this.usageRepo.getAccountUsage(accountId);\n const entitlement = this.needsRemoteEntitlement(usage)\n ? await this.entitlementProvider.getAccountEntitlement(accountId)\n : undefined;\n\n return {\n storageLimitBytes: this.resolve(usage?.storageLimitBytes, entitlement?.quota.storageLimitBytes, 'XPOD_DEFAULT_STORAGE_LIMIT_BYTES'),\n bandwidthLimitBps: this.resolve(usage?.bandwidthLimitBps, entitlement?.quota.bandwidthLimitBps, 'XPOD_DEFAULT_BANDWIDTH_LIMIT_BPS'),\n computeLimitSeconds: this.resolve(usage?.computeLimitSeconds, entitlement?.quota.computeLimitSeconds, 'XPOD_DEFAULT_COMPUTE_LIMIT_SECONDS'),\n tokenLimitMonthly: this.resolve(usage?.tokenLimitMonthly, entitlement?.quota.tokenLimitMonthly, 'XPOD_DEFAULT_TOKEN_LIMIT_MONTHLY'),\n };\n }\n\n public async setAccountQuota(accountId: string, quota: Partial<AccountQuota>): Promise<void> {\n if (quota.storageLimitBytes !== undefined) {\n await this.usageRepo.setAccountStorageLimit(accountId, quota.storageLimitBytes);\n }\n if (quota.bandwidthLimitBps !== undefined) {\n await this.usageRepo.setAccountBandwidthLimit(accountId, quota.bandwidthLimitBps);\n }\n if (quota.computeLimitSeconds !== undefined) {\n await this.usageRepo.setAccountComputeLimit(accountId, quota.computeLimitSeconds);\n }\n if (quota.tokenLimitMonthly !== undefined) {\n await this.usageRepo.setAccountTokenLimit(accountId, quota.tokenLimitMonthly);\n }\n }\n\n public async getPodQuota(podId: string): Promise<AccountQuota> {\n const usage = await this.usageRepo.getPodUsage(podId);\n return {\n storageLimitBytes: this.resolve(usage?.storageLimitBytes, undefined, 'XPOD_DEFAULT_STORAGE_LIMIT_BYTES'),\n bandwidthLimitBps: this.resolve(usage?.bandwidthLimitBps, undefined, 'XPOD_DEFAULT_BANDWIDTH_LIMIT_BPS'),\n computeLimitSeconds: this.resolve(usage?.computeLimitSeconds, undefined, 'XPOD_DEFAULT_COMPUTE_LIMIT_SECONDS'),\n tokenLimitMonthly: this.resolve(usage?.tokenLimitMonthly, undefined, 'XPOD_DEFAULT_TOKEN_LIMIT_MONTHLY'),\n };\n }\n\n public async setPodQuota(podId: string, quota: Partial<AccountQuota>): Promise<void> {\n // Try to get pod info from CSS\n const podInfo = await this.accountRepo.getPodInfo(podId);\n\n // If pod doesn't exist in CSS, try to get accountId from usage table\n let accountId = podInfo?.accountId;\n if (!accountId) {\n const usage = await this.usageRepo.getPodUsage(podId);\n accountId = usage?.accountId;\n }\n\n if (!accountId) {\n throw new Error(`Pod ${podId} not found or has no associated account`);\n }\n\n if (quota.storageLimitBytes !== undefined) {\n await this.usageRepo.setPodStorageLimit(podId, accountId, quota.storageLimitBytes);\n }\n if (quota.bandwidthLimitBps !== undefined) {\n await this.usageRepo.setPodBandwidthLimit(podId, accountId, quota.bandwidthLimitBps);\n }\n if (quota.computeLimitSeconds !== undefined) {\n await this.usageRepo.setPodComputeLimit(podId, accountId, quota.computeLimitSeconds);\n }\n if (quota.tokenLimitMonthly !== undefined) {\n await this.usageRepo.setPodTokenLimit(podId, accountId, quota.tokenLimitMonthly);\n }\n }\n\n public async clearAccountQuota(accountId: string): Promise<void> {\n await this.setAccountQuota(accountId, {\n storageLimitBytes: null,\n bandwidthLimitBps: null,\n computeLimitSeconds: null,\n tokenLimitMonthly: null,\n });\n }\n\n public async clearPodQuota(podId: string): Promise<void> {\n await this.setPodQuota(podId, {\n storageLimitBytes: null,\n bandwidthLimitBps: null,\n computeLimitSeconds: null,\n tokenLimitMonthly: null,\n });\n }\n\n /**\n * 解析配额值:本地自定义配额 > 远端 entitlement > 环境变量默认值 > null (不限制)\n */\n private resolve(dbValue: number | null | undefined, remoteValue: number | null | undefined, envKey: string): number | null {\n // DB 中有明确值(包括 0)\n if (typeof dbValue === 'number') {\n return dbValue;\n }\n if (remoteValue === null || typeof remoteValue === 'number') {\n return remoteValue;\n }\n // 回退到环境变量\n return envNumber(envKey);\n }\n\n private needsRemoteEntitlement(usage: {\n storageLimitBytes?: number | null;\n bandwidthLimitBps?: number | null;\n computeLimitSeconds?: number | null;\n tokenLimitMonthly?: number | null;\n } | undefined): boolean {\n return ![\n usage?.storageLimitBytes,\n usage?.bandwidthLimitBps,\n usage?.computeLimitSeconds,\n usage?.tokenLimitMonthly,\n ].every((value) => typeof value === 'number');\n }\n}\n"]}
|
|
@@ -75,22 +75,6 @@
|
|
|
75
75
|
"@id": "undefineds:dist/quota/DrizzleQuotaService.jsonld#DrizzleQuotaService__member_clearPodQuota",
|
|
76
76
|
"memberFieldName": "clearPodQuota"
|
|
77
77
|
},
|
|
78
|
-
{
|
|
79
|
-
"@id": "undefineds:dist/quota/DrizzleQuotaService.jsonld#DrizzleQuotaService__member_getAccountLimit",
|
|
80
|
-
"memberFieldName": "getAccountLimit"
|
|
81
|
-
},
|
|
82
|
-
{
|
|
83
|
-
"@id": "undefineds:dist/quota/DrizzleQuotaService.jsonld#DrizzleQuotaService__member_getPodLimit",
|
|
84
|
-
"memberFieldName": "getPodLimit"
|
|
85
|
-
},
|
|
86
|
-
{
|
|
87
|
-
"@id": "undefineds:dist/quota/DrizzleQuotaService.jsonld#DrizzleQuotaService__member_setAccountLimit",
|
|
88
|
-
"memberFieldName": "setAccountLimit"
|
|
89
|
-
},
|
|
90
|
-
{
|
|
91
|
-
"@id": "undefineds:dist/quota/DrizzleQuotaService.jsonld#DrizzleQuotaService__member_setPodLimit",
|
|
92
|
-
"memberFieldName": "setPodLimit"
|
|
93
|
-
},
|
|
94
78
|
{
|
|
95
79
|
"@id": "undefineds:dist/quota/DrizzleQuotaService.jsonld#DrizzleQuotaService__member_resolve",
|
|
96
80
|
"memberFieldName": "resolve"
|
|
@@ -6,8 +6,4 @@ export declare class NoopQuotaService implements QuotaService {
|
|
|
6
6
|
setPodQuota(): Promise<void>;
|
|
7
7
|
clearAccountQuota(): Promise<void>;
|
|
8
8
|
clearPodQuota(): Promise<void>;
|
|
9
|
-
getAccountLimit(): Promise<number | null | undefined>;
|
|
10
|
-
getPodLimit(): Promise<number | null | undefined>;
|
|
11
|
-
setAccountLimit(): Promise<void>;
|
|
12
|
-
setPodLimit(): Promise<void>;
|
|
13
9
|
}
|
|
@@ -14,14 +14,6 @@ class NoopQuotaService {
|
|
|
14
14
|
async setPodQuota() { }
|
|
15
15
|
async clearAccountQuota() { }
|
|
16
16
|
async clearPodQuota() { }
|
|
17
|
-
async getAccountLimit() {
|
|
18
|
-
return Number.POSITIVE_INFINITY;
|
|
19
|
-
}
|
|
20
|
-
async getPodLimit() {
|
|
21
|
-
return null;
|
|
22
|
-
}
|
|
23
|
-
async setAccountLimit() { }
|
|
24
|
-
async setPodLimit() { }
|
|
25
17
|
}
|
|
26
18
|
exports.NoopQuotaService = NoopQuotaService;
|
|
27
19
|
//# sourceMappingURL=NoopQuotaService.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NoopQuotaService.js","sourceRoot":"","sources":["../../src/quota/NoopQuotaService.ts"],"names":[],"mappings":";;;AAEA,MAAM,QAAQ,GAAiB;IAC7B,iBAAiB,EAAE,IAAI;IACvB,iBAAiB,EAAE,IAAI;IACvB,mBAAmB,EAAE,IAAI;IACzB,iBAAiB,EAAE,IAAI;CACxB,CAAC;AAEF,MAAa,gBAAgB;IACpB,KAAK,CAAC,eAAe,KAA4B,OAAO,QAAQ,CAAC,CAAC,CAAC;IACnE,KAAK,CAAC,eAAe,KAAmB,CAAC;IACzC,KAAK,CAAC,WAAW,KAA4B,OAAO,QAAQ,CAAC,CAAC,CAAC;IAC/D,KAAK,CAAC,WAAW,KAAmB,CAAC;IACrC,KAAK,CAAC,iBAAiB,KAAmB,CAAC;IAC3C,KAAK,CAAC,aAAa,KAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"NoopQuotaService.js","sourceRoot":"","sources":["../../src/quota/NoopQuotaService.ts"],"names":[],"mappings":";;;AAEA,MAAM,QAAQ,GAAiB;IAC7B,iBAAiB,EAAE,IAAI;IACvB,iBAAiB,EAAE,IAAI;IACvB,mBAAmB,EAAE,IAAI;IACzB,iBAAiB,EAAE,IAAI;CACxB,CAAC;AAEF,MAAa,gBAAgB;IACpB,KAAK,CAAC,eAAe,KAA4B,OAAO,QAAQ,CAAC,CAAC,CAAC;IACnE,KAAK,CAAC,eAAe,KAAmB,CAAC;IACzC,KAAK,CAAC,WAAW,KAA4B,OAAO,QAAQ,CAAC,CAAC,CAAC;IAC/D,KAAK,CAAC,WAAW,KAAmB,CAAC;IACrC,KAAK,CAAC,iBAAiB,KAAmB,CAAC;IAC3C,KAAK,CAAC,aAAa,KAAmB,CAAC;CAC/C;AAPD,4CAOC","sourcesContent":["import type { QuotaService, AccountQuota } from './QuotaService';\n\nconst NO_LIMIT: AccountQuota = {\n storageLimitBytes: null,\n bandwidthLimitBps: null,\n computeLimitSeconds: null,\n tokenLimitMonthly: null,\n};\n\nexport class NoopQuotaService implements QuotaService {\n public async getAccountQuota(): Promise<AccountQuota> { return NO_LIMIT; }\n public async setAccountQuota(): Promise<void> {}\n public async getPodQuota(): Promise<AccountQuota> { return NO_LIMIT; }\n public async setPodQuota(): Promise<void> {}\n public async clearAccountQuota(): Promise<void> {}\n public async clearPodQuota(): Promise<void> {}\n}\n"]}
|
|
@@ -36,22 +36,6 @@
|
|
|
36
36
|
{
|
|
37
37
|
"@id": "undefineds:dist/quota/NoopQuotaService.jsonld#NoopQuotaService__member_clearPodQuota",
|
|
38
38
|
"memberFieldName": "clearPodQuota"
|
|
39
|
-
},
|
|
40
|
-
{
|
|
41
|
-
"@id": "undefineds:dist/quota/NoopQuotaService.jsonld#NoopQuotaService__member_getAccountLimit",
|
|
42
|
-
"memberFieldName": "getAccountLimit"
|
|
43
|
-
},
|
|
44
|
-
{
|
|
45
|
-
"@id": "undefineds:dist/quota/NoopQuotaService.jsonld#NoopQuotaService__member_getPodLimit",
|
|
46
|
-
"memberFieldName": "getPodLimit"
|
|
47
|
-
},
|
|
48
|
-
{
|
|
49
|
-
"@id": "undefineds:dist/quota/NoopQuotaService.jsonld#NoopQuotaService__member_setAccountLimit",
|
|
50
|
-
"memberFieldName": "setAccountLimit"
|
|
51
|
-
},
|
|
52
|
-
{
|
|
53
|
-
"@id": "undefineds:dist/quota/NoopQuotaService.jsonld#NoopQuotaService__member_setPodLimit",
|
|
54
|
-
"memberFieldName": "setPodLimit"
|
|
55
39
|
}
|
|
56
40
|
],
|
|
57
41
|
"constructorArguments": []
|
|
@@ -16,8 +16,4 @@ export interface QuotaService {
|
|
|
16
16
|
setPodQuota(podId: string, quota: Partial<AccountQuota>): Promise<void>;
|
|
17
17
|
clearAccountQuota(accountId: string): Promise<void>;
|
|
18
18
|
clearPodQuota(podId: string): Promise<void>;
|
|
19
|
-
getAccountLimit(accountId: string): Promise<number | null | undefined>;
|
|
20
|
-
getPodLimit(podId: string): Promise<number | null | undefined>;
|
|
21
|
-
setAccountLimit(accountId: string, limit: number | null): Promise<void>;
|
|
22
|
-
setPodLimit(podId: string, limit: number | null): Promise<void>;
|
|
23
19
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"QuotaService.js","sourceRoot":"","sources":["../../src/quota/QuotaService.ts"],"names":[],"mappings":";AAAA;;;;GAIG","sourcesContent":["/**\n * 统一配额模型\n *\n * 支持四种资源:存储、带宽、计算、Token\n */\n\nexport interface AccountQuota {\n storageLimitBytes: number | null;\n bandwidthLimitBps: number | null;\n computeLimitSeconds: number | null;\n tokenLimitMonthly: number | null;\n}\n\nexport interface QuotaService {\n getAccountQuota(accountId: string): Promise<AccountQuota>;\n setAccountQuota(accountId: string, quota: Partial<AccountQuota>): Promise<void>;\n getPodQuota(podId: string): Promise<AccountQuota>;\n setPodQuota(podId: string, quota: Partial<AccountQuota>): Promise<void>;\n clearAccountQuota(accountId: string): Promise<void>;\n clearPodQuota(podId: string): Promise<void>;\n
|
|
1
|
+
{"version":3,"file":"QuotaService.js","sourceRoot":"","sources":["../../src/quota/QuotaService.ts"],"names":[],"mappings":";AAAA;;;;GAIG","sourcesContent":["/**\n * 统一配额模型\n *\n * 支持四种资源:存储、带宽、计算、Token\n */\n\nexport interface AccountQuota {\n storageLimitBytes: number | null;\n bandwidthLimitBps: number | null;\n computeLimitSeconds: number | null;\n tokenLimitMonthly: number | null;\n}\n\nexport interface QuotaService {\n getAccountQuota(accountId: string): Promise<AccountQuota>;\n setAccountQuota(accountId: string, quota: Partial<AccountQuota>): Promise<void>;\n getPodQuota(podId: string): Promise<AccountQuota>;\n setPodQuota(podId: string, quota: Partial<AccountQuota>): Promise<void>;\n clearAccountQuota(accountId: string): Promise<void>;\n clearPodQuota(podId: string): Promise<void>;\n}\n"]}
|
|
@@ -33,22 +33,6 @@
|
|
|
33
33
|
{
|
|
34
34
|
"@id": "undefineds:dist/quota/QuotaService.jsonld#QuotaService__member_clearPodQuota",
|
|
35
35
|
"memberFieldName": "clearPodQuota"
|
|
36
|
-
},
|
|
37
|
-
{
|
|
38
|
-
"@id": "undefineds:dist/quota/QuotaService.jsonld#QuotaService__member_getAccountLimit",
|
|
39
|
-
"memberFieldName": "getAccountLimit"
|
|
40
|
-
},
|
|
41
|
-
{
|
|
42
|
-
"@id": "undefineds:dist/quota/QuotaService.jsonld#QuotaService__member_getPodLimit",
|
|
43
|
-
"memberFieldName": "getPodLimit"
|
|
44
|
-
},
|
|
45
|
-
{
|
|
46
|
-
"@id": "undefineds:dist/quota/QuotaService.jsonld#QuotaService__member_setAccountLimit",
|
|
47
|
-
"memberFieldName": "setAccountLimit"
|
|
48
|
-
},
|
|
49
|
-
{
|
|
50
|
-
"@id": "undefineds:dist/quota/QuotaService.jsonld#QuotaService__member_setPodLimit",
|
|
51
|
-
"memberFieldName": "setPodLimit"
|
|
52
36
|
}
|
|
53
37
|
],
|
|
54
38
|
"constructorArguments": []
|