@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
|
@@ -50,11 +50,11 @@ class QuotaAdminHttpHandler extends community_server_1.HttpHandler {
|
|
|
50
50
|
}
|
|
51
51
|
async handleGet(target, response) {
|
|
52
52
|
if (target.type === 'account') {
|
|
53
|
-
const quota = await this.quotaService.
|
|
53
|
+
const quota = await this.quotaService.getAccountQuota(target.id);
|
|
54
54
|
this.writeJson(response, 200, {
|
|
55
55
|
type: 'account',
|
|
56
56
|
accountId: target.id,
|
|
57
|
-
|
|
57
|
+
quota,
|
|
58
58
|
});
|
|
59
59
|
return;
|
|
60
60
|
}
|
|
@@ -62,13 +62,13 @@ class QuotaAdminHttpHandler extends community_server_1.HttpHandler {
|
|
|
62
62
|
if (!podInfo) {
|
|
63
63
|
throw new community_server_2.BadRequestHttpError('Unknown pod identifier.');
|
|
64
64
|
}
|
|
65
|
-
const quota = await this.quotaService.
|
|
65
|
+
const quota = await this.quotaService.getPodQuota(target.id);
|
|
66
66
|
this.writeJson(response, 200, {
|
|
67
67
|
type: 'pod',
|
|
68
68
|
podId: target.id,
|
|
69
69
|
accountId: podInfo.accountId,
|
|
70
70
|
baseUrl: podInfo.baseUrl ?? null,
|
|
71
|
-
|
|
71
|
+
quota,
|
|
72
72
|
});
|
|
73
73
|
}
|
|
74
74
|
async handlePut(target, request, response) {
|
|
@@ -85,23 +85,23 @@ class QuotaAdminHttpHandler extends community_server_1.HttpHandler {
|
|
|
85
85
|
}
|
|
86
86
|
}
|
|
87
87
|
if (target.type === 'account') {
|
|
88
|
-
await this.quotaService.
|
|
89
|
-
const latest = await this.quotaService.
|
|
88
|
+
await this.quotaService.setAccountQuota(target.id, quota);
|
|
89
|
+
const latest = await this.quotaService.getAccountQuota(target.id);
|
|
90
90
|
this.writeJson(response, 200, {
|
|
91
91
|
status: 'updated',
|
|
92
92
|
targetType: target.type,
|
|
93
93
|
targetId: target.id,
|
|
94
|
-
|
|
94
|
+
quota: latest,
|
|
95
95
|
});
|
|
96
96
|
return;
|
|
97
97
|
}
|
|
98
|
-
await this.quotaService.
|
|
99
|
-
const latest = await this.quotaService.
|
|
98
|
+
await this.quotaService.setPodQuota(target.id, quota);
|
|
99
|
+
const latest = await this.quotaService.getPodQuota(target.id);
|
|
100
100
|
this.writeJson(response, 200, {
|
|
101
101
|
status: 'updated',
|
|
102
102
|
targetType: target.type,
|
|
103
103
|
targetId: target.id,
|
|
104
|
-
|
|
104
|
+
quota: latest,
|
|
105
105
|
});
|
|
106
106
|
}
|
|
107
107
|
async handleDelete(target, response) {
|
|
@@ -112,10 +112,10 @@ class QuotaAdminHttpHandler extends community_server_1.HttpHandler {
|
|
|
112
112
|
}
|
|
113
113
|
}
|
|
114
114
|
if (target.type === 'account') {
|
|
115
|
-
await this.quotaService.
|
|
115
|
+
await this.quotaService.clearAccountQuota(target.id);
|
|
116
116
|
}
|
|
117
117
|
else {
|
|
118
|
-
await this.quotaService.
|
|
118
|
+
await this.quotaService.clearPodQuota(target.id);
|
|
119
119
|
}
|
|
120
120
|
this.writeJson(response, 200, {
|
|
121
121
|
status: 'cleared',
|
|
@@ -175,17 +175,23 @@ class QuotaAdminHttpHandler extends community_server_1.HttpHandler {
|
|
|
175
175
|
}
|
|
176
176
|
}
|
|
177
177
|
extractQuota(body) {
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
178
|
+
const quota = {};
|
|
179
|
+
let hasQuotaField = false;
|
|
180
|
+
for (const field of ['storageLimitBytes', 'bandwidthLimitBps', 'computeLimitSeconds', 'tokenLimitMonthly']) {
|
|
181
|
+
if (!Object.prototype.hasOwnProperty.call(body, field)) {
|
|
182
|
+
continue;
|
|
183
|
+
}
|
|
184
|
+
const value = body[field];
|
|
185
|
+
if (value !== null && (typeof value !== 'number' || !Number.isFinite(value) || value < 0)) {
|
|
186
|
+
throw new community_server_2.BadRequestHttpError(`${field} must be a non-negative number or null.`);
|
|
187
|
+
}
|
|
188
|
+
quota[field] = value;
|
|
189
|
+
hasQuotaField = true;
|
|
184
190
|
}
|
|
185
|
-
if (
|
|
186
|
-
throw new community_server_2.BadRequestHttpError('
|
|
191
|
+
if (!hasQuotaField) {
|
|
192
|
+
throw new community_server_2.BadRequestHttpError('Body must include at least one quota field.');
|
|
187
193
|
}
|
|
188
|
-
return
|
|
194
|
+
return quota;
|
|
189
195
|
}
|
|
190
196
|
writeOptions(response) {
|
|
191
197
|
response.statusCode = 204;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"QuotaAdminHttpHandler.js","sourceRoot":"","sources":["../../../src/http/quota/QuotaAdminHttpHandler.ts"],"names":[],"mappings":"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","sourcesContent":["import { createSolidTokenVerifier } from '@solid/access-token-verifier';\nimport { getLoggerFor } from 'global-logger-factory';\nimport { HttpHandler } from '@solid/community-server';\nimport type { HttpHandlerInput, HttpRequest, HttpResponse } from '@solid/community-server';\nimport {\n \n BadRequestHttpError,\n ForbiddenHttpError,\n MethodNotAllowedHttpError,\n NotImplementedHttpError,\n UnauthorizedHttpError,\n} from '@solid/community-server';\nimport { getIdentityDatabase } from '../../identity/drizzle/db';\nimport { AccountRepository } from '../../identity/drizzle/AccountRepository';\nimport { AccountRoleRepository } from '../../identity/drizzle/AccountRoleRepository';\nimport type { QuotaService } from '../../quota/QuotaService';\n\ninterface QuotaAdminHttpHandlerOptions {\n identityDbUrl: string;\n basePath?: string;\n roleRepository?: AccountRoleRepository;\n quotaService: QuotaService;\n}\n\ntype QuotaTarget =\n | { type: 'account'; id: string }\n | { type: 'pod'; id: string };\n\nexport class QuotaAdminHttpHandler extends HttpHandler {\n protected readonly logger = getLoggerFor(this);\n private readonly verify = createSolidTokenVerifier();\n private readonly accountRepo: AccountRepository;\n private readonly roleRepo: AccountRoleRepository;\n private readonly quotaService: QuotaService;\n private readonly basePath: string;\n\n public constructor(options: QuotaAdminHttpHandlerOptions) {\n super();\n const db = getIdentityDatabase(options.identityDbUrl);\n this.accountRepo = new AccountRepository(db);\n this.roleRepo = options.roleRepository ?? new AccountRoleRepository(db);\n this.basePath = options.basePath ?? '/api/quota/';\n this.quotaService = options.quotaService;\n }\n\n public override async canHandle({ request }: HttpHandlerInput): Promise<void> {\n const path = this.getUrl(request).pathname;\n if (!path.startsWith(this.basePath)) {\n throw new NotImplementedHttpError('Not a quota admin request.');\n }\n }\n\n public override async handle({ request, response }: HttpHandlerInput): Promise<void> {\n const method = (request.method ?? 'GET').toUpperCase();\n if (method === 'OPTIONS') {\n this.writeOptions(response);\n return;\n }\n\n if (![ 'GET', 'PUT', 'DELETE' ].includes(method)) {\n throw new MethodNotAllowedHttpError([ 'GET', 'PUT', 'DELETE', 'OPTIONS' ]);\n }\n\n await this.authenticateAdmin(request);\n\n const target = this.parseTarget(this.getUrl(request).pathname);\n\n switch (method) {\n case 'GET':\n await this.handleGet(target, response);\n break;\n case 'PUT':\n await this.handlePut(target, request, response);\n break;\n case 'DELETE':\n await this.handleDelete(target, response);\n break;\n }\n }\n\n private async handleGet(target: QuotaTarget, response: HttpResponse): Promise<void> {\n if (target.type === 'account') {\n const quota = await this.quotaService.getAccountLimit(target.id);\n this.writeJson(response, 200, {\n type: 'account',\n accountId: target.id,\n quotaLimit: quota ?? null,\n });\n return;\n }\n\n const podInfo = await this.accountRepo.getPodInfo(target.id);\n if (!podInfo) {\n throw new BadRequestHttpError('Unknown pod identifier.');\n }\n const quota = await this.quotaService.getPodLimit(target.id);\n this.writeJson(response, 200, {\n type: 'pod',\n podId: target.id,\n accountId: podInfo.accountId,\n baseUrl: podInfo.baseUrl ?? null,\n quotaLimit: quota ?? null,\n });\n }\n\n private async handlePut(target: QuotaTarget, request: HttpRequest, response: HttpResponse): Promise<void> {\n const body = await this.readJson(request);\n if (body == null || typeof body !== 'object') {\n throw new BadRequestHttpError('Request body must be an object.');\n }\n const payload = body as Record<string, unknown>;\n const quota = this.extractQuota(payload);\n\n if (target.type === 'pod') {\n const podInfo = await this.accountRepo.getPodInfo(target.id);\n if (!podInfo) {\n throw new BadRequestHttpError('Unknown pod identifier.');\n }\n }\n\n if (target.type === 'account') {\n await this.quotaService.setAccountLimit(target.id, quota);\n const latest = await this.quotaService.getAccountLimit(target.id);\n this.writeJson(response, 200, {\n status: 'updated',\n targetType: target.type,\n targetId: target.id,\n quotaLimit: latest ?? null,\n });\n return;\n }\n\n await this.quotaService.setPodLimit(target.id, quota);\n const latest = await this.quotaService.getPodLimit(target.id);\n\n this.writeJson(response, 200, {\n status: 'updated',\n targetType: target.type,\n targetId: target.id,\n quotaLimit: latest ?? null,\n });\n }\n\n private async handleDelete(target: QuotaTarget, response: HttpResponse): Promise<void> {\n if (target.type === 'pod') {\n const podInfo = await this.accountRepo.getPodInfo(target.id);\n if (!podInfo) {\n throw new BadRequestHttpError('Unknown pod identifier.');\n }\n }\n\n if (target.type === 'account') {\n await this.quotaService.setAccountLimit(target.id, null);\n } else {\n await this.quotaService.setPodLimit(target.id, null);\n }\n this.writeJson(response, 200, {\n status: 'cleared',\n targetType: target.type,\n targetId: target.id,\n });\n }\n\n private async authenticateAdmin(request: HttpRequest): Promise<void> {\n const authorization = request.headers.authorization;\n if (!authorization || !authorization.startsWith('Bearer ')) {\n throw new UnauthorizedHttpError('Quota管理接口需要携带 Bearer Token。');\n }\n let payload: Record<string, unknown>;\n try {\n payload = await this.verify(authorization) as unknown as Record<string, unknown>;\n } catch (error: unknown) {\n throw new UnauthorizedHttpError('无法验证访问令牌。', { cause: error });\n }\n const webId = this.extractWebId(payload);\n if (!webId) {\n throw new UnauthorizedHttpError('访问令牌缺少 webid。');\n }\n const context = await this.roleRepo.findByWebId(webId);\n if (!context || !context.roles.includes('admin')) {\n throw new ForbiddenHttpError('仅限管理员修改配额。');\n }\n }\n\n private parseTarget(pathname: string): QuotaTarget {\n const relative = pathname.slice(this.basePath.length);\n const segments = relative.split('/').filter(Boolean);\n if (segments.length !== 2) {\n throw new BadRequestHttpError('Quota path must match /accounts/{id} or /pods/{id}.');\n }\n const [ scope, identifier ] = segments;\n if (!identifier) {\n throw new BadRequestHttpError('Missing identifier.');\n }\n if (scope === 'accounts') {\n return { type: 'account', id: decodeURIComponent(identifier) };\n }\n if (scope === 'pods') {\n return { type: 'pod', id: decodeURIComponent(identifier) };\n }\n throw new BadRequestHttpError('Unknown quota scope.');\n }\n\n private async readJson(request: HttpRequest): Promise<unknown> {\n const body = await this.readBody(request);\n if (!body) {\n return undefined;\n }\n try {\n return JSON.parse(body);\n } catch (error: unknown) {\n throw new BadRequestHttpError('Body must contain valid JSON.', { cause: error });\n }\n }\n\n private extractQuota(body: Record<string, unknown>): number | null {\n if (!Object.prototype.hasOwnProperty.call(body, 'quotaLimit')) {\n throw new BadRequestHttpError('Body must include quotaLimit.');\n }\n const quotaValue = body.quotaLimit as unknown;\n if (quotaValue === null) {\n return null;\n }\n if (typeof quotaValue !== 'number' || !Number.isFinite(quotaValue) || quotaValue < 0) {\n throw new BadRequestHttpError('quotaLimit must be a non-negative number or null.');\n }\n return quotaValue;\n }\n\n private writeOptions(response: HttpResponse): void {\n response.statusCode = 204;\n response.setHeader('Allow', 'GET,PUT,DELETE,OPTIONS');\n response.end();\n }\n\n private writeJson(response: HttpResponse, status: number, payload: unknown): void {\n response.statusCode = status;\n response.setHeader('Content-Type', 'application/json; charset=utf-8');\n response.setHeader('Cache-Control', 'no-store');\n response.end(JSON.stringify(payload));\n }\n\n private async readBody(request: HttpRequest): Promise<string> {\n return await new Promise<string>((resolve, reject) => {\n let data = '';\n request.setEncoding('utf8');\n request.on('data', (chunk: string) => {\n data += chunk;\n });\n request.on('end', () => resolve(data));\n request.on('error', reject);\n });\n }\n\n private getUrl(request: HttpRequest): URL {\n const hostHeader = request.headers.host ?? request.headers.Host ?? 'localhost';\n const protoHeader = request.headers['x-forwarded-proto'] ?? request.headers['X-Forwarded-Proto'];\n const protocol = Array.isArray(protoHeader) ? protoHeader[0] : protoHeader;\n const scheme = typeof protocol === 'string' ? protocol.split(',')[0]?.trim() ?? 'http' : 'http';\n const rawUrl = request.url ?? '/';\n return new URL(rawUrl, `${scheme}://${hostHeader}`);\n }\n\n private extractWebId(payload: Record<string, unknown>): string | undefined {\n const getString = (key: string): string | undefined => {\n const value = payload[key];\n if (typeof value !== 'string') {\n return undefined;\n }\n const trimmed = value.trim();\n return trimmed.length > 0 ? trimmed : undefined;\n };\n const direct = getString('webid') ?? getString('webId') ?? getString('sub');\n if (direct) {\n return direct;\n }\n const clientId = getString('client_id');\n if (clientId && clientId.startsWith('https://')) {\n return clientId;\n }\n return undefined;\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"QuotaAdminHttpHandler.js","sourceRoot":"","sources":["../../../src/http/quota/QuotaAdminHttpHandler.ts"],"names":[],"mappings":"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","sourcesContent":["import { createSolidTokenVerifier } from '@solid/access-token-verifier';\nimport { getLoggerFor } from 'global-logger-factory';\nimport { HttpHandler } from '@solid/community-server';\nimport type { HttpHandlerInput, HttpRequest, HttpResponse } from '@solid/community-server';\nimport {\n BadRequestHttpError,\n ForbiddenHttpError,\n MethodNotAllowedHttpError,\n NotImplementedHttpError,\n UnauthorizedHttpError,\n} from '@solid/community-server';\nimport { getIdentityDatabase } from '../../identity/drizzle/db';\nimport { AccountRepository } from '../../identity/drizzle/AccountRepository';\nimport { AccountRoleRepository } from '../../identity/drizzle/AccountRoleRepository';\nimport type { AccountQuota, QuotaService } from '../../quota/QuotaService';\n\ninterface QuotaAdminHttpHandlerOptions {\n identityDbUrl: string;\n basePath?: string;\n roleRepository?: AccountRoleRepository;\n quotaService: QuotaService;\n}\n\ntype QuotaTarget =\n | { type: 'account'; id: string }\n | { type: 'pod'; id: string };\n\nexport class QuotaAdminHttpHandler extends HttpHandler {\n protected readonly logger = getLoggerFor(this);\n private readonly verify = createSolidTokenVerifier();\n private readonly accountRepo: AccountRepository;\n private readonly roleRepo: AccountRoleRepository;\n private readonly quotaService: QuotaService;\n private readonly basePath: string;\n\n public constructor(options: QuotaAdminHttpHandlerOptions) {\n super();\n const db = getIdentityDatabase(options.identityDbUrl);\n this.accountRepo = new AccountRepository(db);\n this.roleRepo = options.roleRepository ?? new AccountRoleRepository(db);\n this.basePath = options.basePath ?? '/api/quota/';\n this.quotaService = options.quotaService;\n }\n\n public override async canHandle({ request }: HttpHandlerInput): Promise<void> {\n const path = this.getUrl(request).pathname;\n if (!path.startsWith(this.basePath)) {\n throw new NotImplementedHttpError('Not a quota admin request.');\n }\n }\n\n public override async handle({ request, response }: HttpHandlerInput): Promise<void> {\n const method = (request.method ?? 'GET').toUpperCase();\n if (method === 'OPTIONS') {\n this.writeOptions(response);\n return;\n }\n\n if (![ 'GET', 'PUT', 'DELETE' ].includes(method)) {\n throw new MethodNotAllowedHttpError([ 'GET', 'PUT', 'DELETE', 'OPTIONS' ]);\n }\n\n await this.authenticateAdmin(request);\n\n const target = this.parseTarget(this.getUrl(request).pathname);\n\n switch (method) {\n case 'GET':\n await this.handleGet(target, response);\n break;\n case 'PUT':\n await this.handlePut(target, request, response);\n break;\n case 'DELETE':\n await this.handleDelete(target, response);\n break;\n }\n }\n\n private async handleGet(target: QuotaTarget, response: HttpResponse): Promise<void> {\n if (target.type === 'account') {\n const quota = await this.quotaService.getAccountQuota(target.id);\n this.writeJson(response, 200, {\n type: 'account',\n accountId: target.id,\n quota,\n });\n return;\n }\n\n const podInfo = await this.accountRepo.getPodInfo(target.id);\n if (!podInfo) {\n throw new BadRequestHttpError('Unknown pod identifier.');\n }\n const quota = await this.quotaService.getPodQuota(target.id);\n this.writeJson(response, 200, {\n type: 'pod',\n podId: target.id,\n accountId: podInfo.accountId,\n baseUrl: podInfo.baseUrl ?? null,\n quota,\n });\n }\n\n private async handlePut(target: QuotaTarget, request: HttpRequest, response: HttpResponse): Promise<void> {\n const body = await this.readJson(request);\n if (body == null || typeof body !== 'object') {\n throw new BadRequestHttpError('Request body must be an object.');\n }\n const payload = body as Record<string, unknown>;\n const quota = this.extractQuota(payload);\n\n if (target.type === 'pod') {\n const podInfo = await this.accountRepo.getPodInfo(target.id);\n if (!podInfo) {\n throw new BadRequestHttpError('Unknown pod identifier.');\n }\n }\n\n if (target.type === 'account') {\n await this.quotaService.setAccountQuota(target.id, quota);\n const latest = await this.quotaService.getAccountQuota(target.id);\n this.writeJson(response, 200, {\n status: 'updated',\n targetType: target.type,\n targetId: target.id,\n quota: latest,\n });\n return;\n }\n\n await this.quotaService.setPodQuota(target.id, quota);\n const latest = await this.quotaService.getPodQuota(target.id);\n\n this.writeJson(response, 200, {\n status: 'updated',\n targetType: target.type,\n targetId: target.id,\n quota: latest,\n });\n }\n\n private async handleDelete(target: QuotaTarget, response: HttpResponse): Promise<void> {\n if (target.type === 'pod') {\n const podInfo = await this.accountRepo.getPodInfo(target.id);\n if (!podInfo) {\n throw new BadRequestHttpError('Unknown pod identifier.');\n }\n }\n\n if (target.type === 'account') {\n await this.quotaService.clearAccountQuota(target.id);\n } else {\n await this.quotaService.clearPodQuota(target.id);\n }\n this.writeJson(response, 200, {\n status: 'cleared',\n targetType: target.type,\n targetId: target.id,\n });\n }\n\n private async authenticateAdmin(request: HttpRequest): Promise<void> {\n const authorization = request.headers.authorization;\n if (!authorization || !authorization.startsWith('Bearer ')) {\n throw new UnauthorizedHttpError('Quota管理接口需要携带 Bearer Token。');\n }\n let payload: Record<string, unknown>;\n try {\n payload = await this.verify(authorization) as unknown as Record<string, unknown>;\n } catch (error: unknown) {\n throw new UnauthorizedHttpError('无法验证访问令牌。', { cause: error });\n }\n const webId = this.extractWebId(payload);\n if (!webId) {\n throw new UnauthorizedHttpError('访问令牌缺少 webid。');\n }\n const context = await this.roleRepo.findByWebId(webId);\n if (!context || !context.roles.includes('admin')) {\n throw new ForbiddenHttpError('仅限管理员修改配额。');\n }\n }\n\n private parseTarget(pathname: string): QuotaTarget {\n const relative = pathname.slice(this.basePath.length);\n const segments = relative.split('/').filter(Boolean);\n if (segments.length !== 2) {\n throw new BadRequestHttpError('Quota path must match /accounts/{id} or /pods/{id}.');\n }\n const [ scope, identifier ] = segments;\n if (!identifier) {\n throw new BadRequestHttpError('Missing identifier.');\n }\n if (scope === 'accounts') {\n return { type: 'account', id: decodeURIComponent(identifier) };\n }\n if (scope === 'pods') {\n return { type: 'pod', id: decodeURIComponent(identifier) };\n }\n throw new BadRequestHttpError('Unknown quota scope.');\n }\n\n private async readJson(request: HttpRequest): Promise<unknown> {\n const body = await this.readBody(request);\n if (!body) {\n return undefined;\n }\n try {\n return JSON.parse(body);\n } catch (error: unknown) {\n throw new BadRequestHttpError('Body must contain valid JSON.', { cause: error });\n }\n }\n\n private extractQuota(body: Record<string, unknown>): Partial<AccountQuota> {\n const quota: Partial<AccountQuota> = {};\n let hasQuotaField = false;\n for (const field of [ 'storageLimitBytes', 'bandwidthLimitBps', 'computeLimitSeconds', 'tokenLimitMonthly' ] as const) {\n if (!Object.prototype.hasOwnProperty.call(body, field)) {\n continue;\n }\n const value = body[field];\n if (value !== null && (typeof value !== 'number' || !Number.isFinite(value) || value < 0)) {\n throw new BadRequestHttpError(`${field} must be a non-negative number or null.`);\n }\n quota[field] = value;\n hasQuotaField = true;\n }\n if (!hasQuotaField) {\n throw new BadRequestHttpError('Body must include at least one quota field.');\n }\n return quota;\n }\n\n private writeOptions(response: HttpResponse): void {\n response.statusCode = 204;\n response.setHeader('Allow', 'GET,PUT,DELETE,OPTIONS');\n response.end();\n }\n\n private writeJson(response: HttpResponse, status: number, payload: unknown): void {\n response.statusCode = status;\n response.setHeader('Content-Type', 'application/json; charset=utf-8');\n response.setHeader('Cache-Control', 'no-store');\n response.end(JSON.stringify(payload));\n }\n\n private async readBody(request: HttpRequest): Promise<string> {\n return await new Promise<string>((resolve, reject) => {\n let data = '';\n request.setEncoding('utf8');\n request.on('data', (chunk: string) => {\n data += chunk;\n });\n request.on('end', () => resolve(data));\n request.on('error', reject);\n });\n }\n\n private getUrl(request: HttpRequest): URL {\n const hostHeader = request.headers.host ?? request.headers.Host ?? 'localhost';\n const protoHeader = request.headers['x-forwarded-proto'] ?? request.headers['X-Forwarded-Proto'];\n const protocol = Array.isArray(protoHeader) ? protoHeader[0] : protoHeader;\n const scheme = typeof protocol === 'string' ? protocol.split(',')[0]?.trim() ?? 'http' : 'http';\n const rawUrl = request.url ?? '/';\n return new URL(rawUrl, `${scheme}://${hostHeader}`);\n }\n\n private extractWebId(payload: Record<string, unknown>): string | undefined {\n const getString = (key: string): string | undefined => {\n const value = payload[key];\n if (typeof value !== 'string') {\n return undefined;\n }\n const trimmed = value.trim();\n return trimmed.length > 0 ? trimmed : undefined;\n };\n const direct = getString('webid') ?? getString('webId') ?? getString('sub');\n if (direct) {\n return direct;\n }\n const clientId = getString('client_id');\n if (clientId && clientId.startsWith('https://')) {\n return clientId;\n }\n return undefined;\n }\n}\n"]}
|
|
@@ -1,31 +1,13 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export interface PodQuotaContext {
|
|
3
|
-
accountId: string;
|
|
4
|
-
podId: string;
|
|
5
|
-
baseUrl?: string;
|
|
6
|
-
podQuota?: number;
|
|
7
|
-
accountQuota?: number;
|
|
8
|
-
}
|
|
1
|
+
import type { IdentityDatabase } from './db';
|
|
9
2
|
/**
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* Reads account/pod data from CSS's internal_kv table.
|
|
13
|
-
* Quota limits are stored in identity_account_usage / identity_pod_usage tables.
|
|
3
|
+
* Reads account/pod ownership data from the CSS identity facts.
|
|
4
|
+
* Quota values live in identity_usage and are managed by UsageRepository.
|
|
14
5
|
*/
|
|
15
6
|
export declare class AccountRepository {
|
|
16
|
-
private readonly
|
|
17
|
-
private readonly kvTableName;
|
|
18
|
-
private readonly accountUsageTable;
|
|
19
|
-
private readonly podUsageTable;
|
|
7
|
+
private readonly podLookupRepo;
|
|
20
8
|
constructor(db: IdentityDatabase, kvTableName?: string);
|
|
21
|
-
getAccountQuota(accountId: string): Promise<number | undefined>;
|
|
22
9
|
getPodInfo(podId: string): Promise<{
|
|
23
10
|
accountId: string;
|
|
24
|
-
podQuota?: number;
|
|
25
11
|
baseUrl?: string;
|
|
26
12
|
} | undefined>;
|
|
27
|
-
private getPodQuota;
|
|
28
|
-
getQuotaContext(accountId: string, podId: string): Promise<PodQuotaContext | undefined>;
|
|
29
|
-
setAccountQuota(accountId: string, quota?: number): Promise<void>;
|
|
30
|
-
setPodQuota(podId: string, quota?: number): Promise<void>;
|
|
31
13
|
}
|
|
@@ -1,130 +1,26 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.AccountRepository = void 0;
|
|
4
|
-
const
|
|
5
|
-
const db_1 = require("./db");
|
|
4
|
+
const PodLookupRepository_1 = require("./PodLookupRepository");
|
|
6
5
|
/**
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* Reads account/pod data from CSS's internal_kv table.
|
|
10
|
-
* Quota limits are stored in identity_account_usage / identity_pod_usage tables.
|
|
6
|
+
* Reads account/pod ownership data from the CSS identity facts.
|
|
7
|
+
* Quota values live in identity_usage and are managed by UsageRepository.
|
|
11
8
|
*/
|
|
12
9
|
class AccountRepository {
|
|
13
10
|
constructor(db, kvTableName) {
|
|
14
|
-
this.
|
|
15
|
-
this.kvTableName = kvTableName ?? 'internal_kv';
|
|
16
|
-
this.accountUsageTable = 'identity_account_usage';
|
|
17
|
-
this.podUsageTable = 'identity_pod_usage';
|
|
18
|
-
}
|
|
19
|
-
async getAccountQuota(accountId) {
|
|
20
|
-
try {
|
|
21
|
-
const tableId = drizzle_orm_1.sql.identifier([this.accountUsageTable]);
|
|
22
|
-
const result = await (0, db_1.executeQuery)(this.db, (0, drizzle_orm_1.sql) `SELECT storage_limit_bytes FROM ${tableId} WHERE account_id = ${accountId} LIMIT 1`);
|
|
23
|
-
if (result.rows.length === 0) {
|
|
24
|
-
return undefined;
|
|
25
|
-
}
|
|
26
|
-
const limit = result.rows[0].storage_limit_bytes;
|
|
27
|
-
return limit != null ? limit : undefined;
|
|
28
|
-
}
|
|
29
|
-
catch {
|
|
30
|
-
// Table might not exist
|
|
31
|
-
return undefined;
|
|
32
|
-
}
|
|
11
|
+
this.podLookupRepo = new PodLookupRepository_1.PodLookupRepository(db, kvTableName);
|
|
33
12
|
}
|
|
34
13
|
async getPodInfo(podId) {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
SELECT key, value FROM ${kvTableId}
|
|
39
|
-
WHERE key LIKE 'accounts/data/%'
|
|
40
|
-
`);
|
|
41
|
-
for (const row of result.rows) {
|
|
42
|
-
try {
|
|
43
|
-
const accountId = row.key.replace('accounts/data/', '');
|
|
44
|
-
const data = typeof row.value === 'string' ? JSON.parse(row.value) : row.value;
|
|
45
|
-
const podMap = data['**pod**'] || data.pod || {};
|
|
46
|
-
if (podMap[podId]) {
|
|
47
|
-
const pod = podMap[podId];
|
|
48
|
-
const podQuota = await this.getPodQuota(podId);
|
|
49
|
-
return {
|
|
50
|
-
accountId,
|
|
51
|
-
baseUrl: typeof pod.baseUrl === 'string' ? pod.baseUrl : undefined,
|
|
52
|
-
podQuota,
|
|
53
|
-
};
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
catch {
|
|
57
|
-
// Skip malformed entries
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
return undefined;
|
|
61
|
-
}
|
|
62
|
-
async getPodQuota(podId) {
|
|
63
|
-
try {
|
|
64
|
-
const tableId = drizzle_orm_1.sql.identifier([this.podUsageTable]);
|
|
65
|
-
const result = await (0, db_1.executeQuery)(this.db, (0, drizzle_orm_1.sql) `SELECT storage_limit_bytes FROM ${tableId} WHERE pod_id = ${podId} LIMIT 1`);
|
|
66
|
-
if (result.rows.length === 0) {
|
|
67
|
-
return undefined;
|
|
68
|
-
}
|
|
69
|
-
const limit = result.rows[0].storage_limit_bytes;
|
|
70
|
-
return limit != null ? limit : undefined;
|
|
71
|
-
}
|
|
72
|
-
catch {
|
|
73
|
-
// Table might not exist
|
|
74
|
-
return undefined;
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
async getQuotaContext(accountId, podId) {
|
|
78
|
-
const podInfo = await this.getPodInfo(podId);
|
|
79
|
-
if (!podInfo) {
|
|
80
|
-
return undefined;
|
|
81
|
-
}
|
|
82
|
-
if (podInfo.accountId !== accountId) {
|
|
14
|
+
const pod = await this.podLookupRepo.findById(podId)
|
|
15
|
+
?? await this.podLookupRepo.findByResourceIdentifier(podId);
|
|
16
|
+
if (!pod) {
|
|
83
17
|
return undefined;
|
|
84
18
|
}
|
|
85
|
-
const accountQuota = await this.getAccountQuota(accountId);
|
|
86
19
|
return {
|
|
87
|
-
accountId,
|
|
88
|
-
|
|
89
|
-
baseUrl: podInfo.baseUrl,
|
|
90
|
-
podQuota: podInfo.podQuota,
|
|
91
|
-
accountQuota,
|
|
20
|
+
accountId: pod.accountId,
|
|
21
|
+
baseUrl: pod.baseUrl,
|
|
92
22
|
};
|
|
93
23
|
}
|
|
94
|
-
async setAccountQuota(accountId, quota) {
|
|
95
|
-
const tableId = drizzle_orm_1.sql.identifier([this.accountUsageTable]);
|
|
96
|
-
if ((0, db_1.isDatabaseSqlite)(this.db)) {
|
|
97
|
-
await (0, db_1.executeQuery)(this.db, (0, drizzle_orm_1.sql) `
|
|
98
|
-
INSERT INTO ${tableId} (account_id, storage_limit_bytes)
|
|
99
|
-
VALUES (${accountId}, ${quota ?? null})
|
|
100
|
-
ON CONFLICT (account_id) DO UPDATE SET storage_limit_bytes = ${quota ?? null}
|
|
101
|
-
`);
|
|
102
|
-
}
|
|
103
|
-
else {
|
|
104
|
-
await (0, db_1.executeQuery)(this.db, (0, drizzle_orm_1.sql) `
|
|
105
|
-
INSERT INTO ${tableId} (account_id, storage_limit_bytes)
|
|
106
|
-
VALUES (${accountId}, ${quota ?? null})
|
|
107
|
-
ON CONFLICT (account_id) DO UPDATE SET storage_limit_bytes = EXCLUDED.storage_limit_bytes
|
|
108
|
-
`);
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
async setPodQuota(podId, quota) {
|
|
112
|
-
const tableId = drizzle_orm_1.sql.identifier([this.podUsageTable]);
|
|
113
|
-
if ((0, db_1.isDatabaseSqlite)(this.db)) {
|
|
114
|
-
await (0, db_1.executeQuery)(this.db, (0, drizzle_orm_1.sql) `
|
|
115
|
-
INSERT INTO ${tableId} (pod_id, account_id, storage_limit_bytes)
|
|
116
|
-
VALUES (${podId}, '', ${quota ?? null})
|
|
117
|
-
ON CONFLICT (pod_id) DO UPDATE SET storage_limit_bytes = ${quota ?? null}
|
|
118
|
-
`);
|
|
119
|
-
}
|
|
120
|
-
else {
|
|
121
|
-
await (0, db_1.executeQuery)(this.db, (0, drizzle_orm_1.sql) `
|
|
122
|
-
INSERT INTO ${tableId} (pod_id, account_id, storage_limit_bytes)
|
|
123
|
-
VALUES (${podId}, '', ${quota ?? null})
|
|
124
|
-
ON CONFLICT (pod_id) DO UPDATE SET storage_limit_bytes = EXCLUDED.storage_limit_bytes
|
|
125
|
-
`);
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
24
|
}
|
|
129
25
|
exports.AccountRepository = AccountRepository;
|
|
130
26
|
//# sourceMappingURL=AccountRepository.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AccountRepository.js","sourceRoot":"","sources":["../../../src/identity/drizzle/AccountRepository.ts"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"AccountRepository.js","sourceRoot":"","sources":["../../../src/identity/drizzle/AccountRepository.ts"],"names":[],"mappings":";;;AACA,+DAA4D;AAE5D;;;GAGG;AACH,MAAa,iBAAiB;IAG5B,YACE,EAAoB,EACpB,WAAoB;QAEpB,IAAI,CAAC,aAAa,GAAG,IAAI,yCAAmB,CAAC,EAAE,EAAE,WAAW,CAAC,CAAC;IAChE,CAAC;IAEM,KAAK,CAAC,UAAU,CAAC,KAAa;QACnC,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,KAAK,CAAC;eAC/C,MAAM,IAAI,CAAC,aAAa,CAAC,wBAAwB,CAAC,KAAK,CAAC,CAAC;QAC9D,IAAI,CAAC,GAAG,EAAE,CAAC;YACT,OAAO,SAAS,CAAC;QACnB,CAAC;QACD,OAAO;YACL,SAAS,EAAE,GAAG,CAAC,SAAS;YACxB,OAAO,EAAE,GAAG,CAAC,OAAO;SACrB,CAAC;IACJ,CAAC;CACF;AArBD,8CAqBC","sourcesContent":["import type { IdentityDatabase } from './db';\nimport { PodLookupRepository } from './PodLookupRepository';\n\n/**\n * Reads account/pod ownership data from the CSS identity facts.\n * Quota values live in identity_usage and are managed by UsageRepository.\n */\nexport class AccountRepository {\n private readonly podLookupRepo: PodLookupRepository;\n\n public constructor(\n db: IdentityDatabase,\n kvTableName?: string,\n ) {\n this.podLookupRepo = new PodLookupRepository(db, kvTableName);\n }\n\n public async getPodInfo(podId: string): Promise<{ accountId: string; baseUrl?: string } | undefined> {\n const pod = await this.podLookupRepo.findById(podId)\n ?? await this.podLookupRepo.findByResourceIdentifier(podId);\n if (!pod) {\n return undefined;\n }\n return {\n accountId: pod.accountId,\n baseUrl: pod.baseUrl,\n };\n }\n}\n"]}
|
|
@@ -6,18 +6,18 @@ export interface AccountRoleContext {
|
|
|
6
6
|
}
|
|
7
7
|
export declare class AccountRoleRepository {
|
|
8
8
|
private readonly db;
|
|
9
|
-
private readonly ready;
|
|
10
9
|
private readonly logger;
|
|
11
10
|
constructor(db: IdentityDatabase);
|
|
12
11
|
findByAccountId(accountId: string): Promise<AccountRoleContext | undefined>;
|
|
13
12
|
findByWebId(webId: string): Promise<AccountRoleContext | undefined>;
|
|
14
13
|
findByWebIdLoose(webId: string): Promise<AccountRoleContext | undefined>;
|
|
15
14
|
addRoles(accountId: string, roles: string[]): Promise<void>;
|
|
16
|
-
private
|
|
17
|
-
private fetchRoles;
|
|
18
|
-
private getAccountPayloadById;
|
|
15
|
+
private getAccountById;
|
|
19
16
|
private loadAllAccounts;
|
|
17
|
+
private loadIdentityStoreAccounts;
|
|
18
|
+
private loadInternalKvAccounts;
|
|
20
19
|
private loadFileAccountMap;
|
|
20
|
+
private updateAccountRecord;
|
|
21
|
+
private toJsonSql;
|
|
21
22
|
private isTableMissing;
|
|
22
|
-
private isDuplicateDefinitionError;
|
|
23
23
|
}
|