@cobaltcore-dev/aurora 1.3.0 → 2.1.0
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/client/ContentHeader-fKKZVu-o.mjs +116 -0
- package/dist/client/ContentHeader-fKKZVu-o.mjs.map +1 -0
- package/dist/client/DeleteFlavorModal-Be2GFiB_.mjs +802 -0
- package/dist/client/DeleteFlavorModal-Be2GFiB_.mjs.map +1 -0
- package/dist/client/{FiltersInput-CKI6zWnm.mjs → FiltersInput-Becwxo_J.mjs} +36 -33
- package/dist/client/FiltersInput-Becwxo_J.mjs.map +1 -0
- package/dist/client/{FloatingIpActionModals-Ch8uozH9.mjs → FloatingIpActionModals-Cq10WljB.mjs} +2 -2
- package/dist/client/{FloatingIpActionModals-Ch8uozH9.mjs.map → FloatingIpActionModals-Cq10WljB.mjs.map} +1 -1
- package/dist/client/ImageToastNotifications-BWfzOUMc.mjs +1135 -0
- package/dist/client/ImageToastNotifications-BWfzOUMc.mjs.map +1 -0
- package/dist/client/RouteIdLevelDefaultError-DPF_x2tG.mjs +26 -0
- package/dist/client/RouteIdLevelDefaultError-DPF_x2tG.mjs.map +1 -0
- package/dist/client/StorageNotFound-B-zVinVD.mjs +30 -0
- package/dist/client/StorageNotFound-B-zVinVD.mjs.map +1 -0
- package/dist/client/_flavorId-0PioWpEp.mjs +276 -0
- package/dist/client/_flavorId-0PioWpEp.mjs.map +1 -0
- package/dist/client/_flavorId-B7fp6Nk8.mjs +22 -0
- package/dist/client/_flavorId-B7fp6Nk8.mjs.map +1 -0
- package/dist/client/{_flavorId-Dg7MS0Af.mjs → _flavorId-CgueqKTs.mjs} +4 -3
- package/dist/client/_flavorId-CgueqKTs.mjs.map +1 -0
- package/dist/client/{_floatingIpId-DNn6ABCB.mjs → _floatingIpId-B8r64AL9.mjs} +6 -6
- package/dist/client/{_floatingIpId-DNn6ABCB.mjs.map → _floatingIpId-B8r64AL9.mjs.map} +1 -1
- package/dist/client/{_floatingIpId-C9xfRWot.mjs → _floatingIpId-lEobpOER.mjs} +2 -2
- package/dist/client/{_floatingIpId-C9xfRWot.mjs.map → _floatingIpId-lEobpOER.mjs.map} +1 -1
- package/dist/client/{_imageId-8JIbFmGB.mjs → _imageId-Ccv8PedW.mjs} +4 -3
- package/dist/client/_imageId-Ccv8PedW.mjs.map +1 -0
- package/dist/client/_imageId-CiqEQB_J.mjs +558 -0
- package/dist/client/_imageId-CiqEQB_J.mjs.map +1 -0
- package/dist/client/_imageId-D9LR-02T.mjs +22 -0
- package/dist/client/_imageId-D9LR-02T.mjs.map +1 -0
- package/dist/client/{_projectId-DPzv0CCp.mjs → _projectId-BDARsteX.mjs} +4 -4
- package/dist/client/_projectId-BDARsteX.mjs.map +1 -0
- package/dist/client/{_projectId-DfeVJy0G.mjs → _projectId-CtyAHqhH.mjs} +2 -2
- package/dist/client/_projectId-CtyAHqhH.mjs.map +1 -0
- package/dist/client/_projectId-D3VOH5lN.mjs +94 -0
- package/dist/client/_projectId-D3VOH5lN.mjs.map +1 -0
- package/dist/client/{_projectId-B3wmSala.mjs → _projectId-ZBk6vka0.mjs} +83 -81
- package/dist/client/_projectId-ZBk6vka0.mjs.map +1 -0
- package/dist/client/{_securityGroupId-C6eHmrx_.mjs → _securityGroupId-BwgZnoiR.mjs} +2 -2
- package/dist/client/_securityGroupId-BwgZnoiR.mjs.map +1 -0
- package/dist/client/{_securityGroupId-DmplhDgt.mjs → _securityGroupId-Cz2NUHdB.mjs} +750 -741
- package/dist/client/_securityGroupId-Cz2NUHdB.mjs.map +1 -0
- package/dist/client/{_storageType-CzZ4u9x_.mjs → _storageType-BNpP1A2N.mjs} +3 -2
- package/dist/client/_storageType-BNpP1A2N.mjs.map +1 -0
- package/dist/client/_storageType-BxSGKhAI.mjs +15 -0
- package/dist/client/_storageType-BxSGKhAI.mjs.map +1 -0
- package/dist/client/_storageType-DZYCmiLt.mjs +30 -0
- package/dist/client/_storageType-DZYCmiLt.mjs.map +1 -0
- package/dist/client/_storageType-UqAMP-8d.mjs +859 -0
- package/dist/client/_storageType-UqAMP-8d.mjs.map +1 -0
- package/dist/client/_storageType-u51_7OwJ.mjs +7 -0
- package/dist/client/_storageType-u51_7OwJ.mjs.map +1 -0
- package/dist/client/components/{Error → Errors}/RouteError.d.ts +5 -0
- package/dist/client/components/Errors/RouteIdLevelDefaultError.d.ts +18 -0
- package/dist/client/components/Errors/ServiceLevelDefaultError.d.ts +9 -0
- package/dist/client/components/ListToolbar/SelectedFilters.d.ts +7 -2
- package/dist/client/components/ListToolbar/types.d.ts +6 -0
- package/dist/client/components/ListToolbar/valueLabels.d.ts +11 -0
- package/dist/client/{flavors-B1SDOPDu.mjs → flavors-Ce3J-cFa.mjs} +2 -2
- package/dist/client/{flavors-B1SDOPDu.mjs.map → flavors-Ce3J-cFa.mjs.map} +1 -1
- package/dist/client/flavors-D5ThLaXO.mjs +666 -0
- package/dist/client/flavors-D5ThLaXO.mjs.map +1 -0
- package/dist/client/floatingips-Cz9k80cc.mjs.map +1 -1
- package/dist/client/{floatingips-DrCvtXtT.mjs → floatingips-DXnRlLvV.mjs} +7 -7
- package/dist/client/{floatingips-DrCvtXtT.mjs.map → floatingips-DXnRlLvV.mjs.map} +1 -1
- package/dist/client/floatingips-HRbuLxiN.mjs +22 -0
- package/dist/client/floatingips-HRbuLxiN.mjs.map +1 -0
- package/dist/client/floatingips-nN1kQmsu.mjs +14 -0
- package/dist/client/floatingips-nN1kQmsu.mjs.map +1 -0
- package/dist/client/{images-BGQv9kKT.mjs → images-C8M3ysWR.mjs} +810 -770
- package/dist/client/images-C8M3ysWR.mjs.map +1 -0
- package/dist/client/{images-CPmMa4SQ.mjs → images-Cf5R28NR.mjs} +2 -2
- package/dist/client/{images-CPmMa4SQ.mjs.map → images-Cf5R28NR.mjs.map} +1 -1
- package/dist/client/index.js +419 -415
- package/dist/client/index.js.map +1 -1
- package/dist/client/{objects-BR3eeSNH.mjs → objects-BudUAfWY.mjs} +5478 -5519
- package/dist/client/objects-BudUAfWY.mjs.map +1 -0
- package/dist/client/objects-Dc7sKUr8.mjs +67 -0
- package/dist/client/objects-Dc7sKUr8.mjs.map +1 -0
- package/dist/client/objects-Dw7oeTq2.mjs +80 -0
- package/dist/client/objects-Dw7oeTq2.mjs.map +1 -0
- package/dist/client/objects-_g7nGIn5.mjs +25 -0
- package/dist/client/objects-_g7nGIn5.mjs.map +1 -0
- package/dist/client/objects-eBxvY6_n.mjs +78 -0
- package/dist/client/objects-eBxvY6_n.mjs.map +1 -0
- package/dist/client/projects-yiK0HGSA.mjs.map +1 -1
- package/dist/client/{securitygroups-DEJQBcxm.mjs → securitygroups-67fFxiwt.mjs} +2 -2
- package/dist/client/{securitygroups-DEJQBcxm.mjs.map → securitygroups-67fFxiwt.mjs.map} +1 -1
- package/dist/client/securitygroups-CtKne8UQ.mjs.map +1 -1
- package/dist/client/securitygroups-DXrfyuVF.mjs +580 -0
- package/dist/client/securitygroups-DXrfyuVF.mjs.map +1 -0
- package/dist/client/securitygroups-WLB1_IM6.mjs +22 -0
- package/dist/client/securitygroups-WLB1_IM6.mjs.map +1 -0
- package/dist/client/securitygroups-o0IXZM6S.mjs +14 -0
- package/dist/client/securitygroups-o0IXZM6S.mjs.map +1 -0
- package/dist/client/serviceCatalog-B3lKWC9V.mjs +6 -0
- package/dist/client/serviceCatalog-B3lKWC9V.mjs.map +1 -0
- package/dist/client/storageProviders-CJ5rOh13.mjs +15 -0
- package/dist/client/storageProviders-CJ5rOh13.mjs.map +1 -0
- package/dist/client/trpcClient.d.ts +102 -4
- package/dist/client/useModalTracking-BYd37pXm.mjs +37 -0
- package/dist/client/useModalTracking-BYd37pXm.mjs.map +1 -0
- package/dist/client/useSecurityGroupPermissions--hK4aR8Y.mjs +294 -0
- package/dist/client/useSecurityGroupPermissions--hK4aR8Y.mjs.map +1 -0
- package/dist/client/utils/serviceCatalog.d.ts +9 -0
- package/dist/client/utils/storageProviders.d.ts +44 -0
- package/dist/server/index.d.ts +102 -4
- package/dist/server/index.js +1319 -416
- package/package.json +4 -4
- package/dist/client/ContentHeader-CPJbgYWn.mjs +0 -115
- package/dist/client/ContentHeader-CPJbgYWn.mjs.map +0 -1
- package/dist/client/DeleteFlavorModal-V44QCJBd.mjs +0 -773
- package/dist/client/DeleteFlavorModal-V44QCJBd.mjs.map +0 -1
- package/dist/client/FiltersInput-CKI6zWnm.mjs.map +0 -1
- package/dist/client/ImageToastNotifications-m6c3tVPb.mjs +0 -1152
- package/dist/client/ImageToastNotifications-m6c3tVPb.mjs.map +0 -1
- package/dist/client/_flavorId-Dg7MS0Af.mjs.map +0 -1
- package/dist/client/_flavorId-kHl-b8rI.mjs +0 -232
- package/dist/client/_flavorId-kHl-b8rI.mjs.map +0 -1
- package/dist/client/_imageId-8JIbFmGB.mjs.map +0 -1
- package/dist/client/_imageId-BBAlRioC.mjs +0 -574
- package/dist/client/_imageId-BBAlRioC.mjs.map +0 -1
- package/dist/client/_projectId-7Nl1MQTJ.mjs +0 -99
- package/dist/client/_projectId-7Nl1MQTJ.mjs.map +0 -1
- package/dist/client/_projectId-B3wmSala.mjs.map +0 -1
- package/dist/client/_projectId-DPzv0CCp.mjs.map +0 -1
- package/dist/client/_projectId-DfeVJy0G.mjs.map +0 -1
- package/dist/client/_securityGroupId-C6eHmrx_.mjs.map +0 -1
- package/dist/client/_securityGroupId-DmplhDgt.mjs.map +0 -1
- package/dist/client/_storageType-BRWWJtow.mjs +0 -14
- package/dist/client/_storageType-BRWWJtow.mjs.map +0 -1
- package/dist/client/_storageType-C5gS9eut.mjs +0 -845
- package/dist/client/_storageType-C5gS9eut.mjs.map +0 -1
- package/dist/client/_storageType-CzZ4u9x_.mjs.map +0 -1
- package/dist/client/_storageType-zeSZe--V.mjs +0 -7
- package/dist/client/_storageType-zeSZe--V.mjs.map +0 -1
- package/dist/client/flavors-D8_PzQUB.mjs +0 -628
- package/dist/client/flavors-D8_PzQUB.mjs.map +0 -1
- package/dist/client/images-BGQv9kKT.mjs.map +0 -1
- package/dist/client/objects-AERkhfiM.mjs +0 -66
- package/dist/client/objects-AERkhfiM.mjs.map +0 -1
- package/dist/client/objects-BR3eeSNH.mjs.map +0 -1
- package/dist/client/objects-DVccbrL1.mjs +0 -66
- package/dist/client/objects-DVccbrL1.mjs.map +0 -1
- package/dist/client/securitygroups-BjhgLBHm.mjs +0 -620
- package/dist/client/securitygroups-BjhgLBHm.mjs.map +0 -1
- package/dist/client/useModalTracking-BcOKTYRN.mjs +0 -33
- package/dist/client/useModalTracking-BcOKTYRN.mjs.map +0 -1
- package/dist/client/useSecurityGroupPermissions-BOyLQLdC.mjs +0 -227
- package/dist/client/useSecurityGroupPermissions-BOyLQLdC.mjs.map +0 -1
package/dist/server/index.js
CHANGED
|
@@ -7,7 +7,11 @@ var __getProtoOf = Object.getPrototypeOf;
|
|
|
7
7
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
8
|
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
9
9
|
var __commonJS = (cb, mod) => function __require() {
|
|
10
|
-
|
|
10
|
+
try {
|
|
11
|
+
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
|
12
|
+
} catch (e) {
|
|
13
|
+
throw mod = 0, e;
|
|
14
|
+
}
|
|
11
15
|
};
|
|
12
16
|
var __export = (target, all) => {
|
|
13
17
|
for (var name in all)
|
|
@@ -27306,7 +27310,7 @@ var require_pathHelpers = __commonJS({
|
|
|
27306
27310
|
});
|
|
27307
27311
|
exports2.encodeOpenstackPathSegment = encodeOpenstackPathSegment2;
|
|
27308
27312
|
exports2.encodeOpenstackPath = encodeOpenstackPath;
|
|
27309
|
-
exports2.validateAndEncodeResourceId =
|
|
27313
|
+
exports2.validateAndEncodeResourceId = validateAndEncodeResourceId8;
|
|
27310
27314
|
var error_1 = require_error();
|
|
27311
27315
|
function encodeOpenstackPathSegment2(segment, label = "Path segment") {
|
|
27312
27316
|
if (!segment || typeof segment !== "string") {
|
|
@@ -27328,10 +27332,10 @@ var require_pathHelpers = __commonJS({
|
|
|
27328
27332
|
return segments.map((seg, idx) => encodeOpenstackPathSegment2(seg, `Path segment ${idx}`)).join("/");
|
|
27329
27333
|
}
|
|
27330
27334
|
__name(encodeOpenstackPath, "encodeOpenstackPath");
|
|
27331
|
-
function
|
|
27335
|
+
function validateAndEncodeResourceId8(id, resourceType = "Resource") {
|
|
27332
27336
|
return encodeOpenstackPathSegment2(id, `${resourceType} ID`);
|
|
27333
27337
|
}
|
|
27334
|
-
__name(
|
|
27338
|
+
__name(validateAndEncodeResourceId8, "validateAndEncodeResourceId");
|
|
27335
27339
|
}
|
|
27336
27340
|
});
|
|
27337
27341
|
|
|
@@ -28654,6 +28658,22 @@ var openstackErrorMiddleware = t.middleware(async ({ next }) => {
|
|
|
28654
28658
|
}
|
|
28655
28659
|
return result;
|
|
28656
28660
|
});
|
|
28661
|
+
function mapScopeError(error, ctx, opts) {
|
|
28662
|
+
if (error instanceof import_signal_openstack.SignalOpenstackApiError && error.statusCode === 401) {
|
|
28663
|
+
if (ctx.validateSession() === true) {
|
|
28664
|
+
return new import_server.TRPCError({
|
|
28665
|
+
code: "NOT_FOUND",
|
|
28666
|
+
message: opts.notFoundMessage
|
|
28667
|
+
});
|
|
28668
|
+
}
|
|
28669
|
+
return new import_server.TRPCError({
|
|
28670
|
+
code: "UNAUTHORIZED",
|
|
28671
|
+
message: "Your session is no longer valid. Please sign in again."
|
|
28672
|
+
});
|
|
28673
|
+
}
|
|
28674
|
+
return error;
|
|
28675
|
+
}
|
|
28676
|
+
__name(mapScopeError, "mapScopeError");
|
|
28657
28677
|
var router = t.router;
|
|
28658
28678
|
var auroraRouter = t.router;
|
|
28659
28679
|
var mergeRouters = t.mergeRouters;
|
|
@@ -28679,13 +28699,26 @@ var domainScopedInputSchema = import_zod.z.object({
|
|
|
28679
28699
|
var projectScopedProcedure = protectedProcedure.input(projectScopedInputSchema).use(/* @__PURE__ */ __name(async function rescopeToProject(opts) {
|
|
28680
28700
|
const { ctx, next, input } = opts;
|
|
28681
28701
|
const { project_id } = input;
|
|
28682
|
-
|
|
28683
|
-
|
|
28684
|
-
|
|
28702
|
+
let openstackSession;
|
|
28703
|
+
try {
|
|
28704
|
+
openstackSession = await ctx.rescopeSession({
|
|
28705
|
+
projectId: project_id
|
|
28706
|
+
});
|
|
28707
|
+
} catch (error) {
|
|
28708
|
+
throw mapScopeError(error, ctx, {
|
|
28709
|
+
notFoundMessage: "Project not found or not accessible with your current session."
|
|
28710
|
+
});
|
|
28711
|
+
}
|
|
28685
28712
|
if (!openstackSession) {
|
|
28713
|
+
if (ctx.validateSession() === false) {
|
|
28714
|
+
throw new import_server.TRPCError({
|
|
28715
|
+
code: "UNAUTHORIZED",
|
|
28716
|
+
message: "Your session is no longer valid. Please log in again."
|
|
28717
|
+
});
|
|
28718
|
+
}
|
|
28686
28719
|
throw new import_server.TRPCError({
|
|
28687
|
-
code: "
|
|
28688
|
-
message: "
|
|
28720
|
+
code: "NOT_FOUND",
|
|
28721
|
+
message: "Project not found or not accessible with your current session."
|
|
28689
28722
|
});
|
|
28690
28723
|
}
|
|
28691
28724
|
return next({
|
|
@@ -28713,13 +28746,26 @@ var domainScopedProcedure = protectedProcedure.input(domainScopedInputSchema).us
|
|
|
28713
28746
|
message: `Access denied. User does not have access to domain ${domain_id}`
|
|
28714
28747
|
});
|
|
28715
28748
|
}
|
|
28716
|
-
|
|
28717
|
-
|
|
28718
|
-
|
|
28749
|
+
let openstackSession;
|
|
28750
|
+
try {
|
|
28751
|
+
openstackSession = await ctx.rescopeSession({
|
|
28752
|
+
domainId: domain_id
|
|
28753
|
+
});
|
|
28754
|
+
} catch (error) {
|
|
28755
|
+
throw mapScopeError(error, ctx, {
|
|
28756
|
+
notFoundMessage: "Domain not found or not accessible with your current session."
|
|
28757
|
+
});
|
|
28758
|
+
}
|
|
28719
28759
|
if (!openstackSession) {
|
|
28760
|
+
if (ctx.validateSession() === false) {
|
|
28761
|
+
throw new import_server.TRPCError({
|
|
28762
|
+
code: "UNAUTHORIZED",
|
|
28763
|
+
message: "Your session is no longer valid. Please log in again."
|
|
28764
|
+
});
|
|
28765
|
+
}
|
|
28720
28766
|
throw new import_server.TRPCError({
|
|
28721
|
-
code: "
|
|
28722
|
-
message: "
|
|
28767
|
+
code: "NOT_FOUND",
|
|
28768
|
+
message: "Domain not found or not accessible with your current session."
|
|
28723
28769
|
});
|
|
28724
28770
|
}
|
|
28725
28771
|
return next({
|
|
@@ -28781,9 +28827,16 @@ var sessionRouter = {
|
|
|
28781
28827
|
setCurrentScope: protectedProcedure.input(discriminatedSchema).mutation(async ({ input, ctx }) => {
|
|
28782
28828
|
switch (input.type) {
|
|
28783
28829
|
case "domain": {
|
|
28784
|
-
|
|
28785
|
-
|
|
28786
|
-
|
|
28830
|
+
let session;
|
|
28831
|
+
try {
|
|
28832
|
+
session = await ctx.rescopeSession({
|
|
28833
|
+
domainId: input.domainId
|
|
28834
|
+
});
|
|
28835
|
+
} catch (error) {
|
|
28836
|
+
throw mapScopeError(error, ctx, {
|
|
28837
|
+
notFoundMessage: "This domain doesn't exist or is not accessible with your current session. Select an available domain."
|
|
28838
|
+
});
|
|
28839
|
+
}
|
|
28787
28840
|
const token = session?.getToken();
|
|
28788
28841
|
if (!token?.tokenData.domain) {
|
|
28789
28842
|
throw new import_server2.TRPCError({
|
|
@@ -28797,9 +28850,16 @@ var sessionRouter = {
|
|
|
28797
28850
|
};
|
|
28798
28851
|
}
|
|
28799
28852
|
case "project": {
|
|
28800
|
-
|
|
28801
|
-
|
|
28802
|
-
|
|
28853
|
+
let session;
|
|
28854
|
+
try {
|
|
28855
|
+
session = await ctx.rescopeSession({
|
|
28856
|
+
projectId: input.projectId
|
|
28857
|
+
});
|
|
28858
|
+
} catch (error) {
|
|
28859
|
+
throw mapScopeError(error, ctx, {
|
|
28860
|
+
notFoundMessage: "This project doesn't exist or is not accessible from your current domain. Please select a project from your current domain."
|
|
28861
|
+
});
|
|
28862
|
+
}
|
|
28803
28863
|
const token = session?.getToken();
|
|
28804
28864
|
if (!token?.tokenData.project) {
|
|
28805
28865
|
throw new import_server2.TRPCError({
|
|
@@ -29033,7 +29093,13 @@ function filterBySearchParams(items, searchTerm, searchFields) {
|
|
|
29033
29093
|
const searchLower = searchTerm.toLowerCase().trim();
|
|
29034
29094
|
return items.filter((item) => searchFields.some((field) => {
|
|
29035
29095
|
const value = item[field];
|
|
29036
|
-
|
|
29096
|
+
if (typeof value === "string") {
|
|
29097
|
+
return value.toLowerCase().includes(searchLower);
|
|
29098
|
+
}
|
|
29099
|
+
if (typeof value === "number") {
|
|
29100
|
+
return value.toString().includes(searchLower);
|
|
29101
|
+
}
|
|
29102
|
+
return false;
|
|
29037
29103
|
}));
|
|
29038
29104
|
}
|
|
29039
29105
|
__name(filterBySearchParams, "filterBySearchParams");
|
|
@@ -29986,7 +30052,10 @@ var imageRouter = {
|
|
|
29986
30052
|
let filteredImages = allImages;
|
|
29987
30053
|
if (hasSearchTerm) {
|
|
29988
30054
|
filteredImages = filterBySearchParams(filteredImages, queryInput.name, [
|
|
29989
|
-
"
|
|
30055
|
+
"id",
|
|
30056
|
+
"name",
|
|
30057
|
+
"owner",
|
|
30058
|
+
"size"
|
|
29990
30059
|
]);
|
|
29991
30060
|
}
|
|
29992
30061
|
if (queryInput.visibility && queryInput.visibility !== "all") {
|
|
@@ -30066,7 +30135,10 @@ var imageRouter = {
|
|
|
30066
30135
|
let filteredImages = allImages;
|
|
30067
30136
|
if (queryInput.name && queryInput.name.trim()) {
|
|
30068
30137
|
filteredImages = filterBySearchParams(filteredImages, queryInput.name, [
|
|
30069
|
-
"
|
|
30138
|
+
"id",
|
|
30139
|
+
"name",
|
|
30140
|
+
"owner",
|
|
30141
|
+
"size"
|
|
30070
30142
|
]);
|
|
30071
30143
|
}
|
|
30072
30144
|
if (queryInput.visibility && queryInput.visibility !== "all") {
|
|
@@ -30605,6 +30677,7 @@ var imageRouter = {
|
|
|
30605
30677
|
});
|
|
30606
30678
|
if (name) {
|
|
30607
30679
|
filteredImages = filterBySearchParams(filteredImages, name, [
|
|
30680
|
+
"id",
|
|
30608
30681
|
"name"
|
|
30609
30682
|
]);
|
|
30610
30683
|
}
|
|
@@ -31168,9 +31241,18 @@ function includesSearchTerm(flavor, searchTerm) {
|
|
|
31168
31241
|
const searchableValues = [
|
|
31169
31242
|
flavor.id,
|
|
31170
31243
|
flavor.name,
|
|
31171
|
-
flavor.description
|
|
31244
|
+
flavor.description,
|
|
31245
|
+
flavor.vcpus,
|
|
31246
|
+
flavor.ram,
|
|
31247
|
+
flavor.disk,
|
|
31248
|
+
flavor.swap
|
|
31172
31249
|
];
|
|
31173
|
-
return searchableValues.some((value) =>
|
|
31250
|
+
return searchableValues.some((value) => {
|
|
31251
|
+
if (value == null) return false;
|
|
31252
|
+
if (typeof value === "string") return regex.test(value);
|
|
31253
|
+
if (typeof value === "number") return regex.test(value.toString());
|
|
31254
|
+
return false;
|
|
31255
|
+
});
|
|
31174
31256
|
}
|
|
31175
31257
|
__name(includesSearchTerm, "includesSearchTerm");
|
|
31176
31258
|
function filterAndSortFlavors(flavors, searchTerm, sortBy, sortDirection) {
|
|
@@ -31191,6 +31273,26 @@ function filterAndSortFlavors(flavors, searchTerm, sortBy, sortDirection) {
|
|
|
31191
31273
|
return result;
|
|
31192
31274
|
}
|
|
31193
31275
|
__name(filterAndSortFlavors, "filterAndSortFlavors");
|
|
31276
|
+
function supportsDescriptionField(microversion) {
|
|
31277
|
+
const [major, minor] = microversion.split(".").map(Number);
|
|
31278
|
+
return major > 2 || major === 2 && minor >= 55;
|
|
31279
|
+
}
|
|
31280
|
+
__name(supportsDescriptionField, "supportsDescriptionField");
|
|
31281
|
+
async function getMaxMicroversion(compute) {
|
|
31282
|
+
try {
|
|
31283
|
+
const response = await compute.get("");
|
|
31284
|
+
const data = await response.json();
|
|
31285
|
+
if (data.version) {
|
|
31286
|
+
return data.version.version || "2.1";
|
|
31287
|
+
} else if (data.versions) {
|
|
31288
|
+
const currentVersion = data.versions.find((v) => v.status === "CURRENT");
|
|
31289
|
+
return currentVersion?.version || "2.1";
|
|
31290
|
+
}
|
|
31291
|
+
} catch {
|
|
31292
|
+
}
|
|
31293
|
+
return "2.1";
|
|
31294
|
+
}
|
|
31295
|
+
__name(getMaxMicroversion, "getMaxMicroversion");
|
|
31194
31296
|
async function getFlavorById(compute, flavorId) {
|
|
31195
31297
|
if (!flavorId || flavorId.trim() === "") {
|
|
31196
31298
|
throw new import_server7.TRPCError({
|
|
@@ -31213,7 +31315,13 @@ async function getFlavorById(compute, flavorId) {
|
|
|
31213
31315
|
}
|
|
31214
31316
|
let response;
|
|
31215
31317
|
try {
|
|
31216
|
-
|
|
31318
|
+
const maxVersion = await getMaxMicroversion(compute);
|
|
31319
|
+
const options = supportsDescriptionField(maxVersion) ? {
|
|
31320
|
+
headers: {
|
|
31321
|
+
"OpenStack-API-Version": `compute ${maxVersion}`
|
|
31322
|
+
}
|
|
31323
|
+
} : void 0;
|
|
31324
|
+
response = await compute.get(`flavors/${encodedId}`, options);
|
|
31217
31325
|
} catch (error) {
|
|
31218
31326
|
if (error instanceof import_server7.TRPCError) throw error;
|
|
31219
31327
|
const statusCode = getStatusCodeFromError(error);
|
|
@@ -31245,11 +31353,20 @@ __name(getFlavorById, "getFlavorById");
|
|
|
31245
31353
|
async function fetchFlavors(compute, isPublic) {
|
|
31246
31354
|
let response;
|
|
31247
31355
|
try {
|
|
31248
|
-
|
|
31356
|
+
const maxVersion = await getMaxMicroversion(compute);
|
|
31357
|
+
const options = supportsDescriptionField(maxVersion) ? {
|
|
31249
31358
|
queryParams: {
|
|
31250
31359
|
is_public: isPublic
|
|
31360
|
+
},
|
|
31361
|
+
headers: {
|
|
31362
|
+
"OpenStack-API-Version": `compute ${maxVersion}`
|
|
31251
31363
|
}
|
|
31252
|
-
}
|
|
31364
|
+
} : {
|
|
31365
|
+
queryParams: {
|
|
31366
|
+
is_public: isPublic
|
|
31367
|
+
}
|
|
31368
|
+
};
|
|
31369
|
+
response = await compute.get("flavors/detail", options);
|
|
31253
31370
|
} catch (error) {
|
|
31254
31371
|
if (error instanceof import_server7.TRPCError) throw error;
|
|
31255
31372
|
const statusCode = getStatusCodeFromError(error);
|
|
@@ -31278,16 +31395,32 @@ async function fetchFlavors(compute, isPublic) {
|
|
|
31278
31395
|
}
|
|
31279
31396
|
}
|
|
31280
31397
|
__name(fetchFlavors, "fetchFlavors");
|
|
31281
|
-
async function createFlavor(compute, flavorData) {
|
|
31398
|
+
async function createFlavor(compute, flavorData, microversion) {
|
|
31282
31399
|
const requestBody = {
|
|
31283
31400
|
flavor: flavorData
|
|
31284
31401
|
};
|
|
31402
|
+
const headers = {};
|
|
31403
|
+
if (microversion && flavorData.description && supportsDescriptionField(microversion)) {
|
|
31404
|
+
headers["OpenStack-API-Version"] = `compute ${microversion}`;
|
|
31405
|
+
}
|
|
31285
31406
|
let response;
|
|
31286
31407
|
try {
|
|
31287
|
-
response = await compute.post("flavors", requestBody)
|
|
31408
|
+
response = await compute.post("flavors", requestBody, Object.keys(headers).length ? {
|
|
31409
|
+
headers
|
|
31410
|
+
} : void 0);
|
|
31288
31411
|
} catch (error) {
|
|
31289
31412
|
if (error instanceof import_server7.TRPCError) throw error;
|
|
31290
31413
|
const statusCode = getStatusCodeFromError(error);
|
|
31414
|
+
if (statusCode === 400) {
|
|
31415
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
31416
|
+
if (errorMessage.includes("description")) {
|
|
31417
|
+
throw new import_server7.TRPCError({
|
|
31418
|
+
code: "BAD_REQUEST",
|
|
31419
|
+
message: errorMessage,
|
|
31420
|
+
cause: error
|
|
31421
|
+
});
|
|
31422
|
+
}
|
|
31423
|
+
}
|
|
31291
31424
|
handleHttpError(statusCode, CREATE_FLAVOR_STATUS_MAP, ERROR_CODES.CREATE_FLAVOR_FAILED);
|
|
31292
31425
|
}
|
|
31293
31426
|
if (!response.ok) {
|
|
@@ -31298,7 +31431,6 @@ async function createFlavor(compute, flavorData) {
|
|
|
31298
31431
|
const jsonData = JSON.parse(rawData);
|
|
31299
31432
|
return jsonData.flavor;
|
|
31300
31433
|
} catch (error) {
|
|
31301
|
-
console.error("Error parsing flavor response:", error);
|
|
31302
31434
|
throw new import_server7.TRPCError({
|
|
31303
31435
|
code: "INTERNAL_SERVER_ERROR",
|
|
31304
31436
|
message: ERROR_CODES.CREATE_FLAVOR_FAILED,
|
|
@@ -31579,6 +31711,38 @@ __name(removeTenantAccess, "removeTenantAccess");
|
|
|
31579
31711
|
var import_server8 = require("@trpc/server");
|
|
31580
31712
|
var import_signal_openstack3 = __toESM(require_esm());
|
|
31581
31713
|
var flavorRouter = {
|
|
31714
|
+
getComputeApiVersion: projectScopedProcedure.input(import_zod9.z.object({
|
|
31715
|
+
project_id: import_zod9.z.string()
|
|
31716
|
+
})).query(async ({ ctx }) => {
|
|
31717
|
+
try {
|
|
31718
|
+
const compute = ctx.openstack?.service("compute");
|
|
31719
|
+
if (!compute) {
|
|
31720
|
+
throw new import_server8.TRPCError({
|
|
31721
|
+
code: "INTERNAL_SERVER_ERROR",
|
|
31722
|
+
message: ERROR_CODES.COMPUTE_SERVICE_UNAVAILABLE
|
|
31723
|
+
});
|
|
31724
|
+
}
|
|
31725
|
+
const response = await compute.get("");
|
|
31726
|
+
const data = await response.json();
|
|
31727
|
+
let versionString = "2.1";
|
|
31728
|
+
if (data.version) {
|
|
31729
|
+
versionString = data.version.version || "2.1";
|
|
31730
|
+
} else if (data.versions) {
|
|
31731
|
+
const currentVersion = data.versions.find((v) => v.status === "CURRENT");
|
|
31732
|
+
versionString = currentVersion?.version || "2.1";
|
|
31733
|
+
}
|
|
31734
|
+
return {
|
|
31735
|
+
version: versionString,
|
|
31736
|
+
supportsDescription: supportsDescriptionField(versionString)
|
|
31737
|
+
};
|
|
31738
|
+
} catch (error) {
|
|
31739
|
+
console.error("Failed to detect Nova version:", error);
|
|
31740
|
+
return {
|
|
31741
|
+
version: "2.1",
|
|
31742
|
+
supportsDescription: false
|
|
31743
|
+
};
|
|
31744
|
+
}
|
|
31745
|
+
}),
|
|
31582
31746
|
getFlavorById: projectScopedProcedure.input(import_zod9.z.object({
|
|
31583
31747
|
project_id: import_zod9.z.string(),
|
|
31584
31748
|
flavorId: import_zod9.z.string()
|
|
@@ -31666,7 +31830,8 @@ var flavorRouter = {
|
|
|
31666
31830
|
swap: import_zod9.z.number().optional(),
|
|
31667
31831
|
rxtx_factor: import_zod9.z.number().optional(),
|
|
31668
31832
|
"OS-FLV-EXT-DATA:ephemeral": import_zod9.z.number().optional(),
|
|
31669
|
-
"os-flavor-access:is_public": import_zod9.z.boolean().optional()
|
|
31833
|
+
"os-flavor-access:is_public": import_zod9.z.boolean().optional(),
|
|
31834
|
+
description: import_zod9.z.string().optional()
|
|
31670
31835
|
})
|
|
31671
31836
|
})).mutation(async ({ input, ctx }) => {
|
|
31672
31837
|
try {
|
|
@@ -31678,11 +31843,35 @@ var flavorRouter = {
|
|
|
31678
31843
|
message: ERROR_CODES.COMPUTE_SERVICE_UNAVAILABLE
|
|
31679
31844
|
});
|
|
31680
31845
|
}
|
|
31846
|
+
let supportsDescription = false;
|
|
31847
|
+
let detectedVersion = "2.1";
|
|
31848
|
+
try {
|
|
31849
|
+
const versionResponse = await compute.get("");
|
|
31850
|
+
const versionData = await versionResponse.json();
|
|
31851
|
+
let versionString = "2.1";
|
|
31852
|
+
if (versionData.version) {
|
|
31853
|
+
versionString = versionData.version.version || "2.1";
|
|
31854
|
+
} else if (versionData.versions) {
|
|
31855
|
+
const currentVersion = versionData.versions.find((v) => v.status === "CURRENT");
|
|
31856
|
+
versionString = currentVersion?.version || "2.1";
|
|
31857
|
+
}
|
|
31858
|
+
detectedVersion = versionString;
|
|
31859
|
+
supportsDescription = supportsDescriptionField(versionString);
|
|
31860
|
+
} catch {
|
|
31861
|
+
supportsDescription = false;
|
|
31862
|
+
}
|
|
31863
|
+
const flavorToSend = supportsDescription ? flavor : {
|
|
31864
|
+
...flavor
|
|
31865
|
+
};
|
|
31866
|
+
if (!supportsDescription) {
|
|
31867
|
+
const { description, ...rest } = flavorToSend;
|
|
31868
|
+
Object.assign(flavorToSend, rest);
|
|
31869
|
+
}
|
|
31681
31870
|
const flavorData = {
|
|
31682
|
-
...
|
|
31871
|
+
...flavorToSend,
|
|
31683
31872
|
"OS-FLV-EXT-DATA:ephemeral": flavor["OS-FLV-EXT-DATA:ephemeral"] || 0
|
|
31684
31873
|
};
|
|
31685
|
-
const result = await createFlavor(compute, flavorData);
|
|
31874
|
+
const result = await createFlavor(compute, flavorData, supportsDescription ? detectedVersion : void 0);
|
|
31686
31875
|
return result;
|
|
31687
31876
|
} catch (error) {
|
|
31688
31877
|
if (error instanceof import_server8.TRPCError) {
|
|
@@ -32490,7 +32679,7 @@ function buildObjectMetadataHeaders(options) {
|
|
|
32490
32679
|
}
|
|
32491
32680
|
__name(buildObjectMetadataHeaders, "buildObjectMetadataHeaders");
|
|
32492
32681
|
function mapErrorResponseToTRPCError2(error, context) {
|
|
32493
|
-
const { operation, container, object, additionalInfo } = context;
|
|
32682
|
+
const { operation, container, object, additionalInfo, preconditionFailedMeansAlreadyExists } = context;
|
|
32494
32683
|
const baseMessage = `Failed to ${operation}`;
|
|
32495
32684
|
const containerInfo = container ? ` container: ${container}` : "";
|
|
32496
32685
|
const objectInfo = object ? `, object: ${object}` : "";
|
|
@@ -32521,6 +32710,14 @@ function mapErrorResponseToTRPCError2(error, context) {
|
|
|
32521
32710
|
code: "CONFLICT",
|
|
32522
32711
|
message: `Conflict - ${operation}${containerInfo}${objectInfo}${extraInfo}`
|
|
32523
32712
|
});
|
|
32713
|
+
case 412:
|
|
32714
|
+
return preconditionFailedMeansAlreadyExists ? new import_server10.TRPCError({
|
|
32715
|
+
code: "CONFLICT",
|
|
32716
|
+
message: `Conflict - ${operation}${containerInfo}${objectInfo} - already exists${extraInfo}`
|
|
32717
|
+
}) : new import_server10.TRPCError({
|
|
32718
|
+
code: "PRECONDITION_FAILED",
|
|
32719
|
+
message: `Precondition failed - ${operation}${containerInfo}${objectInfo}${extraInfo}`
|
|
32720
|
+
});
|
|
32524
32721
|
case 413:
|
|
32525
32722
|
return new import_server10.TRPCError({
|
|
32526
32723
|
code: "PAYLOAD_TOO_LARGE",
|
|
@@ -32814,6 +33011,11 @@ var createContainerInputSchema = baseContainerInputSchema.extend({
|
|
|
32814
33011
|
tempUrlKey: import_zod11.z.string().optional(),
|
|
32815
33012
|
tempUrlKey2: import_zod11.z.string().optional()
|
|
32816
33013
|
});
|
|
33014
|
+
var createContainerResultSchema = import_zod11.z.object({
|
|
33015
|
+
created: import_zod11.z.literal(true),
|
|
33016
|
+
optionsApplied: import_zod11.z.boolean(),
|
|
33017
|
+
optionsError: import_zod11.z.string().optional()
|
|
33018
|
+
});
|
|
32817
33019
|
var updateContainerMetadataInputSchema = baseContainerInputSchema.extend({
|
|
32818
33020
|
metadata: import_zod11.z.record(import_zod11.z.string(), import_zod11.z.string()).optional(),
|
|
32819
33021
|
removeMetadata: import_zod11.z.array(import_zod11.z.string()).optional(),
|
|
@@ -33151,21 +33353,54 @@ var swiftRouter = {
|
|
|
33151
33353
|
createContainer: projectScopedProcedure.input(projectScopedInputSchema.extend(createContainerInputSchema.shape)).mutation(async ({ input, ctx }) => {
|
|
33152
33354
|
return withErrorHandling2(async () => {
|
|
33153
33355
|
const { account, container, ...options } = input;
|
|
33356
|
+
const { storagePolicy, ...applyOptions } = options;
|
|
33154
33357
|
const openstackSession = ctx.openstack;
|
|
33155
33358
|
const swift = openstackSession?.service("swift");
|
|
33156
33359
|
validateSwiftService(swift);
|
|
33157
|
-
const headers = buildContainerMetadataHeaders(options);
|
|
33158
33360
|
const accountPath = account || "";
|
|
33159
33361
|
const url = accountPath ? `${accountPath}/${encodeURIComponent(container)}` : encodeURIComponent(container);
|
|
33160
|
-
|
|
33161
|
-
|
|
33362
|
+
const probeHeaders = buildContainerMetadataHeaders({
|
|
33363
|
+
storagePolicy
|
|
33364
|
+
});
|
|
33365
|
+
const response = await swift.put(url, void 0, {
|
|
33366
|
+
headers: probeHeaders
|
|
33162
33367
|
}).catch((error) => {
|
|
33163
33368
|
throw mapErrorResponseToTRPCError2(error, {
|
|
33164
33369
|
operation: "create container",
|
|
33165
33370
|
container
|
|
33166
33371
|
});
|
|
33167
33372
|
});
|
|
33168
|
-
|
|
33373
|
+
if (response.status === 202) {
|
|
33374
|
+
throw new import_server11.TRPCError({
|
|
33375
|
+
code: "CONFLICT",
|
|
33376
|
+
message: `Conflict - create container - container already exists: ${container}`
|
|
33377
|
+
});
|
|
33378
|
+
}
|
|
33379
|
+
const optionHeaders = buildContainerMetadataHeaders(applyOptions);
|
|
33380
|
+
if (Object.keys(optionHeaders).length === 0) {
|
|
33381
|
+
return {
|
|
33382
|
+
created: true,
|
|
33383
|
+
optionsApplied: true
|
|
33384
|
+
};
|
|
33385
|
+
}
|
|
33386
|
+
let optionsError;
|
|
33387
|
+
await swift.post(url, void 0, {
|
|
33388
|
+
headers: optionHeaders
|
|
33389
|
+
}).catch((error) => {
|
|
33390
|
+
optionsError = mapErrorResponseToTRPCError2(error, {
|
|
33391
|
+
operation: "apply container settings",
|
|
33392
|
+
container,
|
|
33393
|
+
additionalInfo: "container was created but its metadata/ACL/quota could not be applied"
|
|
33394
|
+
}).message;
|
|
33395
|
+
});
|
|
33396
|
+
return optionsError === void 0 ? {
|
|
33397
|
+
created: true,
|
|
33398
|
+
optionsApplied: true
|
|
33399
|
+
} : {
|
|
33400
|
+
created: true,
|
|
33401
|
+
optionsApplied: false,
|
|
33402
|
+
optionsError
|
|
33403
|
+
};
|
|
33169
33404
|
}, "create container");
|
|
33170
33405
|
}),
|
|
33171
33406
|
/**
|
|
@@ -33591,7 +33826,8 @@ var swiftRouter = {
|
|
|
33591
33826
|
const normalizedPath = normalizeFolderPath(folderPath);
|
|
33592
33827
|
const headers = {
|
|
33593
33828
|
"Content-Type": "application/directory",
|
|
33594
|
-
"Content-Length": "0"
|
|
33829
|
+
"Content-Length": "0",
|
|
33830
|
+
"If-None-Match": "*"
|
|
33595
33831
|
};
|
|
33596
33832
|
if (metadata) {
|
|
33597
33833
|
Object.entries(metadata).forEach(([key, value]) => {
|
|
@@ -33600,14 +33836,14 @@ var swiftRouter = {
|
|
|
33600
33836
|
}
|
|
33601
33837
|
const accountPath = account || "";
|
|
33602
33838
|
const url = accountPath ? `${accountPath}/${encodeURIComponent(container)}/${encodeURIComponent(normalizedPath)}` : `${encodeURIComponent(container)}/${encodeURIComponent(normalizedPath)}`;
|
|
33603
|
-
await swift.put(url, {
|
|
33604
|
-
headers
|
|
33605
|
-
body: new ArrayBuffer(0)
|
|
33839
|
+
await swift.put(url, new ArrayBuffer(0), {
|
|
33840
|
+
headers
|
|
33606
33841
|
}).catch((error) => {
|
|
33607
33842
|
throw mapErrorResponseToTRPCError2(error, {
|
|
33608
33843
|
operation: "create folder",
|
|
33609
33844
|
container,
|
|
33610
|
-
object: normalizedPath
|
|
33845
|
+
object: normalizedPath,
|
|
33846
|
+
preconditionFailedMeansAlreadyExists: true
|
|
33611
33847
|
});
|
|
33612
33848
|
});
|
|
33613
33849
|
return true;
|
|
@@ -34368,6 +34604,16 @@ __name(resolveEC2Credential, "resolveEC2Credential");
|
|
|
34368
34604
|
|
|
34369
34605
|
// src/server/Storage/clients/s3Client.ts
|
|
34370
34606
|
var import_client_s3 = require("@aws-sdk/client-s3");
|
|
34607
|
+
|
|
34608
|
+
// src/server/Storage/constants.ts
|
|
34609
|
+
var S3_MAX_KEYS_PER_REQUEST = 1e3;
|
|
34610
|
+
var S3_PRESIGN_MAX_EXPIRY_SECONDS = 604800;
|
|
34611
|
+
var S3_MAX_SCAN_PAGES = 20;
|
|
34612
|
+
var S3_CONNECTION_TIMEOUT_MS = 5e3;
|
|
34613
|
+
var S3_MAX_BUFFERED_VERSIONS_PER_KEY = 2e4;
|
|
34614
|
+
var MAX_REPORTED_DELETE_ERRORS = 100;
|
|
34615
|
+
|
|
34616
|
+
// src/server/Storage/clients/s3Client.ts
|
|
34371
34617
|
function createS3Client(access, secret, endpoint, region = "default") {
|
|
34372
34618
|
if (!access?.trim()) {
|
|
34373
34619
|
throw new Error("S3 access key is required");
|
|
@@ -34385,7 +34631,10 @@ function createS3Client(access, secret, endpoint, region = "default") {
|
|
|
34385
34631
|
accessKeyId: access,
|
|
34386
34632
|
secretAccessKey: secret
|
|
34387
34633
|
},
|
|
34388
|
-
forcePathStyle: true
|
|
34634
|
+
forcePathStyle: true,
|
|
34635
|
+
requestHandler: {
|
|
34636
|
+
connectionTimeout: S3_CONNECTION_TIMEOUT_MS
|
|
34637
|
+
}
|
|
34389
34638
|
});
|
|
34390
34639
|
}
|
|
34391
34640
|
__name(createS3Client, "createS3Client");
|
|
@@ -34496,6 +34745,8 @@ var cephUploadProcedure = protectedProcedure.use(/* @__PURE__ */ __name(async fu
|
|
|
34496
34745
|
var import_server13 = require("@trpc/server");
|
|
34497
34746
|
var import_signal_openstack4 = __toESM(require_esm());
|
|
34498
34747
|
var S3_ERROR_MAP = {
|
|
34748
|
+
NotFound: "NOT_FOUND",
|
|
34749
|
+
Forbidden: "FORBIDDEN",
|
|
34499
34750
|
NoSuchBucket: "NOT_FOUND",
|
|
34500
34751
|
NoSuchKey: "NOT_FOUND",
|
|
34501
34752
|
NoSuchUpload: "NOT_FOUND",
|
|
@@ -34506,6 +34757,7 @@ var S3_ERROR_MAP = {
|
|
|
34506
34757
|
BucketAlreadyExists: "CONFLICT",
|
|
34507
34758
|
BucketAlreadyOwnedByYou: "CONFLICT",
|
|
34508
34759
|
BucketNotEmpty: "PRECONDITION_FAILED",
|
|
34760
|
+
PreconditionFailed: "CONFLICT",
|
|
34509
34761
|
InvalidBucketState: "BAD_REQUEST",
|
|
34510
34762
|
VersioningNotEnabled: "PRECONDITION_FAILED",
|
|
34511
34763
|
AccessDenied: "FORBIDDEN",
|
|
@@ -34530,7 +34782,7 @@ function mapS3ErrorToTRPCError(error, context) {
|
|
|
34530
34782
|
}
|
|
34531
34783
|
const s3Error = error;
|
|
34532
34784
|
const errorCode = s3Error.Code ?? s3Error.name ?? "";
|
|
34533
|
-
const trpcCode = S3_ERROR_MAP[errorCode] ?? "INTERNAL_SERVER_ERROR";
|
|
34785
|
+
const trpcCode = errorCode === "PreconditionFailed" && !context.preconditionFailedMeansAlreadyExists ? "PRECONDITION_FAILED" : S3_ERROR_MAP[errorCode] ?? "INTERNAL_SERVER_ERROR";
|
|
34534
34786
|
if (!S3_ERROR_MAP[errorCode] && errorCode) {
|
|
34535
34787
|
import_signal_openstack4.logger.warn("Unmapped S3 error code", {
|
|
34536
34788
|
errorCode,
|
|
@@ -34549,6 +34801,8 @@ function mapS3ErrorToTRPCError(error, context) {
|
|
|
34549
34801
|
parts.push("Invalid access key \u2014 your credentials may be expired or incorrect");
|
|
34550
34802
|
} else if (errorCode === "BucketNotEmpty") {
|
|
34551
34803
|
parts.push("The bucket is not empty. Some objects or versions may still exist. Use 'Empty Bucket' first to delete all contents.");
|
|
34804
|
+
} else if (errorCode === "PreconditionFailed") {
|
|
34805
|
+
parts.push(context.preconditionFailedMeansAlreadyExists ? "The object already exists" : "Precondition failed");
|
|
34552
34806
|
} else if (s3Error.message) {
|
|
34553
34807
|
parts.push(s3Error.message);
|
|
34554
34808
|
}
|
|
@@ -34565,12 +34819,6 @@ __name(mapS3ErrorToTRPCError, "mapS3ErrorToTRPCError");
|
|
|
34565
34819
|
|
|
34566
34820
|
// src/server/Storage/types/ceph.ts
|
|
34567
34821
|
var import_zod13 = require("zod");
|
|
34568
|
-
|
|
34569
|
-
// src/server/Storage/constants.ts
|
|
34570
|
-
var S3_MAX_KEYS_PER_REQUEST = 1e3;
|
|
34571
|
-
var S3_PRESIGN_MAX_EXPIRY_SECONDS = 604800;
|
|
34572
|
-
|
|
34573
|
-
// src/server/Storage/types/ceph.ts
|
|
34574
34822
|
var ec2CredentialSchema = import_zod13.z.object({
|
|
34575
34823
|
id: import_zod13.z.string(),
|
|
34576
34824
|
access: import_zod13.z.string(),
|
|
@@ -34606,6 +34854,59 @@ var createBucketOutputSchema = import_zod13.z.object({
|
|
|
34606
34854
|
var deleteBucketInputSchema = projectScopedInputSchema.extend({
|
|
34607
34855
|
bucketName: import_zod13.z.string().min(1)
|
|
34608
34856
|
});
|
|
34857
|
+
var headBucketInputSchema = projectScopedInputSchema.extend({
|
|
34858
|
+
bucketName: existingBucketNameSchema
|
|
34859
|
+
});
|
|
34860
|
+
var bucketStateInputSchema = projectScopedInputSchema.extend({
|
|
34861
|
+
bucketName: existingBucketNameSchema
|
|
34862
|
+
});
|
|
34863
|
+
var bucketStateOutputSchema = import_zod13.z.object({
|
|
34864
|
+
/**
|
|
34865
|
+
* Raw versioning status, straight from `GetBucketVersioning` — the same value
|
|
34866
|
+
* `versioning.getStatus` reports.
|
|
34867
|
+
*
|
|
34868
|
+
* Exposed here so a caller that needs to tell "Suspended" from "Unversioned" (the bucket
|
|
34869
|
+
* header renders a different badge for each) does not have to issue a second
|
|
34870
|
+
* `GetBucketVersioning` alongside this one. `isVersioningEnabled` below is the collapsed
|
|
34871
|
+
* form most callers actually want.
|
|
34872
|
+
*/
|
|
34873
|
+
status: import_zod13.z.enum([
|
|
34874
|
+
"Enabled",
|
|
34875
|
+
"Suspended",
|
|
34876
|
+
"Unversioned"
|
|
34877
|
+
]),
|
|
34878
|
+
isVersioningEnabled: import_zod13.z.boolean(),
|
|
34879
|
+
/**
|
|
34880
|
+
* No current (non-delete-marker) objects. Always exact, never affected by `isPartialScan`:
|
|
34881
|
+
* it comes from a dedicated one-key `ListObjectsV2`, which by definition lists only current
|
|
34882
|
+
* objects and omits delete markers, so "no keys returned" *is* emptiness.
|
|
34883
|
+
*
|
|
34884
|
+
* Deliberately a boolean, not a count - no caller ever needed more than the yes/no, and a
|
|
34885
|
+
* one-key probe has no count to give.
|
|
34886
|
+
*/
|
|
34887
|
+
isEmpty: import_zod13.z.boolean(),
|
|
34888
|
+
/**
|
|
34889
|
+
* Every key in the bucket is hidden behind a delete marker: versioning is on, the scan saw
|
|
34890
|
+
* entries, and none of them was a real (non-delete-marker) version.
|
|
34891
|
+
*
|
|
34892
|
+
* Forced to `false` under `isPartialScan`, because a truncated scan that happened to see only
|
|
34893
|
+
* delete markers cannot tell "no real versions exist" from "no real versions yet".
|
|
34894
|
+
*/
|
|
34895
|
+
hasOnlyDeleteMarkers: import_zod13.z.boolean(),
|
|
34896
|
+
/** At least one non-current version or delete marker exists. Reliable when true. */
|
|
34897
|
+
hasOldVersionsOrDeleteMarkers: import_zod13.z.boolean(),
|
|
34898
|
+
/**
|
|
34899
|
+
* True when the version-history scan stopped before it could rule the history flags in either
|
|
34900
|
+
* direction: it hit the page ceiling, the request was aborted, or S3 returned a truncated
|
|
34901
|
+
* page carrying no continuation marker to follow.
|
|
34902
|
+
*
|
|
34903
|
+
* It says nothing about `isEmpty` or `isVersioningEnabled`, which are established by their own
|
|
34904
|
+
* single requests. Of the two flags it does cover, `hasOldVersionsOrDeleteMarkers: true`
|
|
34905
|
+
* remains reliable - finding one such entry is proof - while `false` means "none found so
|
|
34906
|
+
* far", and `hasOnlyDeleteMarkers` is forced to `false` outright.
|
|
34907
|
+
*/
|
|
34908
|
+
isPartialScan: import_zod13.z.boolean()
|
|
34909
|
+
});
|
|
34609
34910
|
var s3StatusSchema = import_zod13.z.object({
|
|
34610
34911
|
hasCredentials: import_zod13.z.boolean()
|
|
34611
34912
|
});
|
|
@@ -34673,6 +34974,9 @@ var deleteVersionsBulkInputSchema = projectScopedInputSchema.extend({
|
|
|
34673
34974
|
versionId: import_zod13.z.string().min(1)
|
|
34674
34975
|
})).min(1).max(1e4)
|
|
34675
34976
|
});
|
|
34977
|
+
var deleteNonCurrentVersionsInputSchema = projectScopedInputSchema.extend({
|
|
34978
|
+
containerName: existingBucketNameSchema
|
|
34979
|
+
});
|
|
34676
34980
|
var deletedObjectSchema = import_zod13.z.object({
|
|
34677
34981
|
key: import_zod13.z.string(),
|
|
34678
34982
|
versionId: import_zod13.z.string().optional(),
|
|
@@ -34691,6 +34995,22 @@ var deleteObjectsBulkOutputSchema = import_zod13.z.object({
|
|
|
34691
34995
|
deletedCount: import_zod13.z.number().int().nonnegative(),
|
|
34692
34996
|
errorCount: import_zod13.z.number().int().nonnegative()
|
|
34693
34997
|
});
|
|
34998
|
+
var deleteNonCurrentVersionsOutputSchema = import_zod13.z.object({
|
|
34999
|
+
errors: import_zod13.z.array(deleteObjectErrorSchema),
|
|
35000
|
+
deletedCount: import_zod13.z.number().int().nonnegative(),
|
|
35001
|
+
errorCount: import_zod13.z.number().int().nonnegative(),
|
|
35002
|
+
/**
|
|
35003
|
+
* The run did not cover the whole bucket, so non-current versions may survive.
|
|
35004
|
+
* Set when the request was aborted (including during the last page's deletes),
|
|
35005
|
+
* when S3 returned a truncated page without a continuation marker, and when a
|
|
35006
|
+
* key had to be skipped (NoCurrentVersion, MissingVersionId, TooManyVersions) -
|
|
35007
|
+
* the last of which also records an entry in `errors`, so a caller must treat
|
|
35008
|
+
* this flag and `errorCount` as independent rather than as alternatives.
|
|
35009
|
+
* The counts remain accurate for what was actually deleted - this flag only
|
|
35010
|
+
* denies that the wipe was exhaustive, which a bare success count would imply.
|
|
35011
|
+
*/
|
|
35012
|
+
isPartial: import_zod13.z.boolean()
|
|
35013
|
+
});
|
|
34694
35014
|
var createFolderInputSchema2 = projectScopedInputSchema.extend({
|
|
34695
35015
|
containerName: import_zod13.z.string().min(1),
|
|
34696
35016
|
folderPath: import_zod13.z.string().min(1)
|
|
@@ -34809,19 +35129,6 @@ var bucketPolicyStatementSchema = import_zod13.z.object({
|
|
|
34809
35129
|
message: "Principal object must contain at least one of: AWS, Service, or Federated"
|
|
34810
35130
|
});
|
|
34811
35131
|
}
|
|
34812
|
-
if (typeof val === "object" && val.AWS) {
|
|
34813
|
-
const arns = Array.isArray(val.AWS) ? val.AWS : [
|
|
34814
|
-
val.AWS
|
|
34815
|
-
];
|
|
34816
|
-
for (const arn of arns) {
|
|
34817
|
-
if (arn !== "*" && !/^arn:aws:iam::\d{12}:(?:root|user\/.+|role\/.+)$/.test(arn)) {
|
|
34818
|
-
ctx.addIssue({
|
|
34819
|
-
code: "custom",
|
|
34820
|
-
message: `Invalid AWS principal ARN format: ${arn}. Expected arn:aws:iam::ACCOUNT-ID:root or arn:aws:iam::ACCOUNT-ID:(user|role)/NAME`
|
|
34821
|
-
});
|
|
34822
|
-
}
|
|
34823
|
-
}
|
|
34824
|
-
}
|
|
34825
35132
|
}),
|
|
34826
35133
|
NotPrincipal: import_zod13.z.union([
|
|
34827
35134
|
import_zod13.z.string(),
|
|
@@ -35218,6 +35525,193 @@ var containerRouter = {
|
|
|
35218
35525
|
}
|
|
35219
35526
|
}),
|
|
35220
35527
|
/**
|
|
35528
|
+
* Existence probe for a single bucket — one HeadBucket request, no body.
|
|
35529
|
+
*
|
|
35530
|
+
* Exists for route guards that must answer "is there such a bucket?" before rendering a
|
|
35531
|
+
* bucket page. `list` would answer it too, but ListBuckets only returns buckets the
|
|
35532
|
+
* caller *owns*: a bucket reachable through a policy would read as missing. HeadBucket
|
|
35533
|
+
* asks about the bucket itself, so 404 means 404 and 403 means 403.
|
|
35534
|
+
*
|
|
35535
|
+
* @throws TRPCError NOT_FOUND - bucket does not exist (SDK error name `NotFound`)
|
|
35536
|
+
* @throws TRPCError FORBIDDEN - no credentials, or the bucket exists but is not readable
|
|
35537
|
+
*/
|
|
35538
|
+
head: cephProtectedProcedure.input(headBucketInputSchema).query(async ({ ctx, input }) => {
|
|
35539
|
+
const s3 = ctx.getCephClient();
|
|
35540
|
+
const { bucketName } = input;
|
|
35541
|
+
try {
|
|
35542
|
+
await s3.send(new import_client_s32.HeadBucketCommand({
|
|
35543
|
+
Bucket: bucketName
|
|
35544
|
+
}));
|
|
35545
|
+
return {
|
|
35546
|
+
exists: true
|
|
35547
|
+
};
|
|
35548
|
+
} catch (error) {
|
|
35549
|
+
throw mapS3ErrorToTRPCError(error, {
|
|
35550
|
+
operation: "head bucket",
|
|
35551
|
+
bucket: bucketName
|
|
35552
|
+
});
|
|
35553
|
+
}
|
|
35554
|
+
}),
|
|
35555
|
+
/**
|
|
35556
|
+
* Authoritative bucket emptiness/version state.
|
|
35557
|
+
*
|
|
35558
|
+
* Replaces three separate client-side probes (`useBucketInfo`, `DeleteBucketModal`,
|
|
35559
|
+
* `EmptyBucketModal`) that each read only the first page of `objects.list` and derived
|
|
35560
|
+
* `isBucketEmpty`/`hasOldVersionsOrDeleteMarkers` from it — silently wrong for buckets whose
|
|
35561
|
+
* first 100 (or 1000) entries don't tell the whole story.
|
|
35562
|
+
*
|
|
35563
|
+
* Cost, honestly:
|
|
35564
|
+
* - unversioned bucket: **exactly two** requests, whatever its size. Without a version
|
|
35565
|
+
* history there is nothing to scan, so both history flags are false by definition.
|
|
35566
|
+
* - versioned bucket with both an old version (or delete marker) and a surviving real
|
|
35567
|
+
* version: **three** — one page settles both history flags and the scan stops.
|
|
35568
|
+
* - versioned bucket whose history is clean: the scan has nothing to find and no way to know
|
|
35569
|
+
* that early, so it runs to the end of the bucket or to `S3_MAX_SCAN_PAGES`.
|
|
35570
|
+
* - versioned bucket holding *only* delete markers, with no surviving non-current version —
|
|
35571
|
+
* what a `NoncurrentVersionExpiration` lifecycle rule leaves behind once it has expired the
|
|
35572
|
+
* real versions: same cost as the clean-history shape, and for the same reason. The scan
|
|
35573
|
+
* sees `hasOldVersionsOrDeleteMarkers` on page one but can only prove `hasOnlyDeleteMarkers`
|
|
35574
|
+
* by reaching the end, since any later page could still hold a real version.
|
|
35575
|
+
*
|
|
35576
|
+
* The last two are the shapes that cost real round-trips. Both are bounded, and in both the
|
|
35577
|
+
* scan is doing work that has an answer to produce — it is not spinning to re-learn something
|
|
35578
|
+
* already known. Callers that force `staleTime: 0` (the two destructive modals, deliberately)
|
|
35579
|
+
* pay it on every open.
|
|
35580
|
+
*
|
|
35581
|
+
* `isEmpty` is deliberately established by its own one-key `ListObjectsV2` rather than by the
|
|
35582
|
+
* scan: that command lists current objects only, so it answers emptiness exactly, in one
|
|
35583
|
+
* request, on a bucket of any size. That matters beyond speed — emptiness gates "Delete
|
|
35584
|
+
* Bucket", and deriving it from a scan that may be truncated left large buckets permanently
|
|
35585
|
+
* unconfirmable and therefore permanently undeletable, with a "refresh and try again" that
|
|
35586
|
+
* could never succeed.
|
|
35587
|
+
*
|
|
35588
|
+
* Versioning status is read from S3 here, not taken from client input, so the answer can't be
|
|
35589
|
+
* computed against a stale client-cached status. It is also returned raw as `status`, so a
|
|
35590
|
+
* caller needing the three-way value has no reason to issue a second `GetBucketVersioning`
|
|
35591
|
+
* against `versioning.getStatus` alongside this call.
|
|
35592
|
+
*
|
|
35593
|
+
* @throws TRPCError NOT_FOUND - bucket does not exist
|
|
35594
|
+
* @throws TRPCError FORBIDDEN - no credentials or access denied
|
|
35595
|
+
*/
|
|
35596
|
+
getState: cephProtectedProcedure.input(bucketStateInputSchema).query(async ({ ctx, input }) => {
|
|
35597
|
+
const s3 = ctx.getCephClient();
|
|
35598
|
+
const { bucketName } = input;
|
|
35599
|
+
let status;
|
|
35600
|
+
let isVersioningEnabled;
|
|
35601
|
+
let isEmpty;
|
|
35602
|
+
try {
|
|
35603
|
+
const [versioningResponse, currentObjectsResponse] = await Promise.all([
|
|
35604
|
+
s3.send(new import_client_s32.GetBucketVersioningCommand({
|
|
35605
|
+
Bucket: bucketName
|
|
35606
|
+
}), {
|
|
35607
|
+
abortSignal: ctx.req.signal
|
|
35608
|
+
}),
|
|
35609
|
+
s3.send(new import_client_s32.ListObjectsV2Command({
|
|
35610
|
+
Bucket: bucketName,
|
|
35611
|
+
MaxKeys: 1
|
|
35612
|
+
}), {
|
|
35613
|
+
abortSignal: ctx.req.signal
|
|
35614
|
+
})
|
|
35615
|
+
]);
|
|
35616
|
+
status = versioningResponse.Status ?? "Unversioned";
|
|
35617
|
+
isVersioningEnabled = status === "Enabled" || status === "Suspended";
|
|
35618
|
+
isEmpty = (currentObjectsResponse.KeyCount ?? currentObjectsResponse.Contents?.length ?? 0) === 0;
|
|
35619
|
+
} catch (error) {
|
|
35620
|
+
console.error("getState: failed to read bucket versioning status or emptiness", {
|
|
35621
|
+
bucket: bucketName,
|
|
35622
|
+
error
|
|
35623
|
+
});
|
|
35624
|
+
throw mapS3ErrorToTRPCError(error, {
|
|
35625
|
+
operation: "get bucket state",
|
|
35626
|
+
bucket: bucketName
|
|
35627
|
+
});
|
|
35628
|
+
}
|
|
35629
|
+
if (!isVersioningEnabled) {
|
|
35630
|
+
return bucketStateOutputSchema.parse({
|
|
35631
|
+
status,
|
|
35632
|
+
isVersioningEnabled: false,
|
|
35633
|
+
isEmpty,
|
|
35634
|
+
hasOnlyDeleteMarkers: false,
|
|
35635
|
+
hasOldVersionsOrDeleteMarkers: false,
|
|
35636
|
+
isPartialScan: false
|
|
35637
|
+
});
|
|
35638
|
+
}
|
|
35639
|
+
let hasOldVersionOrDeleteMarker = false;
|
|
35640
|
+
let hasRealVersion = false;
|
|
35641
|
+
let anyEntrySeen = false;
|
|
35642
|
+
let keyMarker;
|
|
35643
|
+
let versionIdMarker;
|
|
35644
|
+
let pages = 0;
|
|
35645
|
+
let isPartialScan = false;
|
|
35646
|
+
while (true) {
|
|
35647
|
+
if (ctx.req.signal?.aborted) {
|
|
35648
|
+
isPartialScan = true;
|
|
35649
|
+
break;
|
|
35650
|
+
}
|
|
35651
|
+
let response;
|
|
35652
|
+
try {
|
|
35653
|
+
response = await s3.send(new import_client_s32.ListObjectVersionsCommand({
|
|
35654
|
+
Bucket: bucketName,
|
|
35655
|
+
MaxKeys: S3_MAX_KEYS_PER_REQUEST,
|
|
35656
|
+
KeyMarker: keyMarker,
|
|
35657
|
+
VersionIdMarker: versionIdMarker
|
|
35658
|
+
}), {
|
|
35659
|
+
abortSignal: ctx.req.signal
|
|
35660
|
+
});
|
|
35661
|
+
} catch (error) {
|
|
35662
|
+
if (ctx.req.signal?.aborted) {
|
|
35663
|
+
isPartialScan = true;
|
|
35664
|
+
break;
|
|
35665
|
+
}
|
|
35666
|
+
console.error("getState: version scan failed", {
|
|
35667
|
+
bucket: bucketName,
|
|
35668
|
+
pages,
|
|
35669
|
+
error
|
|
35670
|
+
});
|
|
35671
|
+
throw mapS3ErrorToTRPCError(error, {
|
|
35672
|
+
operation: "get bucket state",
|
|
35673
|
+
bucket: bucketName
|
|
35674
|
+
});
|
|
35675
|
+
}
|
|
35676
|
+
for (const v of response.Versions ?? []) {
|
|
35677
|
+
anyEntrySeen = true;
|
|
35678
|
+
hasRealVersion = true;
|
|
35679
|
+
if (!v.IsLatest) {
|
|
35680
|
+
hasOldVersionOrDeleteMarker = true;
|
|
35681
|
+
}
|
|
35682
|
+
}
|
|
35683
|
+
if ((response.DeleteMarkers?.length ?? 0) > 0) {
|
|
35684
|
+
anyEntrySeen = true;
|
|
35685
|
+
hasOldVersionOrDeleteMarker = true;
|
|
35686
|
+
}
|
|
35687
|
+
pages++;
|
|
35688
|
+
if (hasOldVersionOrDeleteMarker && hasRealVersion) {
|
|
35689
|
+
break;
|
|
35690
|
+
}
|
|
35691
|
+
if (!response.IsTruncated) {
|
|
35692
|
+
break;
|
|
35693
|
+
}
|
|
35694
|
+
if (!response.NextKeyMarker) {
|
|
35695
|
+
isPartialScan = true;
|
|
35696
|
+
break;
|
|
35697
|
+
}
|
|
35698
|
+
if (pages >= S3_MAX_SCAN_PAGES) {
|
|
35699
|
+
isPartialScan = true;
|
|
35700
|
+
break;
|
|
35701
|
+
}
|
|
35702
|
+
keyMarker = response.NextKeyMarker;
|
|
35703
|
+
versionIdMarker = response.NextVersionIdMarker;
|
|
35704
|
+
}
|
|
35705
|
+
return bucketStateOutputSchema.parse({
|
|
35706
|
+
status,
|
|
35707
|
+
isVersioningEnabled,
|
|
35708
|
+
isEmpty,
|
|
35709
|
+
hasOnlyDeleteMarkers: !isPartialScan && anyEntrySeen && !hasRealVersion,
|
|
35710
|
+
hasOldVersionsOrDeleteMarkers: hasOldVersionOrDeleteMarker,
|
|
35711
|
+
isPartialScan
|
|
35712
|
+
});
|
|
35713
|
+
}),
|
|
35714
|
+
/**
|
|
35221
35715
|
* Create a new S3 bucket.
|
|
35222
35716
|
*
|
|
35223
35717
|
* Uses AWS SDK CreateBucketCommand. The AWS SDK automatically adds LocationConstraint
|
|
@@ -35866,11 +36360,239 @@ var objectRouter = {
|
|
|
35866
36360
|
return bulkDeleteItems(s3, containerName, uniqueVersions, ctx.req.signal, "delete versions");
|
|
35867
36361
|
}),
|
|
35868
36362
|
/**
|
|
36363
|
+
* Delete every non-current version and delete marker in a bucket, keeping
|
|
36364
|
+
* each key's current live version intact ("Delete Versions" bucket action).
|
|
36365
|
+
*
|
|
36366
|
+
* Deliberately NOT built on top of `deleteAll` (which the "Empty Bucket"
|
|
36367
|
+
* checkbox also uses): that procedure's full-wipe branch discards
|
|
36368
|
+
* `IsLatest` entirely and re-scans from scratch whenever a page is
|
|
36369
|
+
* exhausted, specifically so it can catch delete markers created by its
|
|
36370
|
+
* own non-versioned-delete branch. Bolting a third "keep the latest"
|
|
36371
|
+
* mode onto that already-intricate loop would risk regressing the other
|
|
36372
|
+
* two. This procedure keeps its own single-pass scan instead.
|
|
36373
|
+
*
|
|
36374
|
+
* ⚠️ IsLatest correctness across pages:
|
|
36375
|
+
* `IsLatest` is computed by S3 at *list time*, not stored permanently. If a
|
|
36376
|
+
* key's versions are split across two list pages and we deleted anything
|
|
36377
|
+
* from that key after page 1 but before fetching page 2, the remaining
|
|
36378
|
+
* version would be re-evaluated as IsLatest=true on page 2 - resurrecting
|
|
36379
|
+
* a version (or worse, a deleted object) that should have stayed removed.
|
|
36380
|
+
* To avoid this, a key's records are only ever acted on once its full
|
|
36381
|
+
* group has been seen: the last key-group on every truncated page is
|
|
36382
|
+
* buffered (`pendingKey`/`pendingItems`) and merged with the next page's
|
|
36383
|
+
* leading group (S3 returns Versions/DeleteMarkers sorted by key, so a
|
|
36384
|
+
* split key's remaining records always appear at the very start of the
|
|
36385
|
+
* next page) before that group is decided on and deleted.
|
|
36386
|
+
*
|
|
36387
|
+
* Per completed key-group: if the current (IsLatest) record is a regular
|
|
36388
|
+
* version, every other record in the group is deleted and the current
|
|
36389
|
+
* version is kept. If the current record is a delete marker, the entire
|
|
36390
|
+
* group - including the marker - is deleted, since a versioned bucket has
|
|
36391
|
+
* no other way to represent "this key was deleted" once its history is
|
|
36392
|
+
* gone; keeping the marker without its superseded versions would leave the
|
|
36393
|
+
* key listed in the "Deleted" tab with nothing left to restore.
|
|
36394
|
+
*
|
|
36395
|
+
* No second pass is needed (unlike `deleteAll`'s full-wipe branch): every
|
|
36396
|
+
* deletion here targets a specific `VersionId`, which never creates a new
|
|
36397
|
+
* delete marker, so nothing new can appear behind the scan as it
|
|
36398
|
+
* progresses. One pass over the bucket is exhaustive.
|
|
36399
|
+
*
|
|
36400
|
+
* @throws TRPCError NOT_FOUND - bucket does not exist
|
|
36401
|
+
* @throws TRPCError FORBIDDEN - no credentials or access denied
|
|
36402
|
+
*/
|
|
36403
|
+
deleteNonCurrentVersions: cephProtectedProcedure.input(deleteNonCurrentVersionsInputSchema).mutation(async ({ ctx, input }) => {
|
|
36404
|
+
const s3 = ctx.getCephClient();
|
|
36405
|
+
const { containerName } = input;
|
|
36406
|
+
const errors = [];
|
|
36407
|
+
let deletedCount = 0;
|
|
36408
|
+
let errorCount = 0;
|
|
36409
|
+
let isPartial = false;
|
|
36410
|
+
const recordErrors = /* @__PURE__ */ __name((newErrors) => {
|
|
36411
|
+
errorCount += newErrors.length;
|
|
36412
|
+
for (const error of newErrors) {
|
|
36413
|
+
if (errors.length >= MAX_REPORTED_DELETE_ERRORS) break;
|
|
36414
|
+
errors.push(error);
|
|
36415
|
+
}
|
|
36416
|
+
}, "recordErrors");
|
|
36417
|
+
let keyMarker;
|
|
36418
|
+
let versionIdMarker;
|
|
36419
|
+
let pendingKey;
|
|
36420
|
+
let pendingItems = [];
|
|
36421
|
+
let sameMarkerCount = 0;
|
|
36422
|
+
const MAX_SAME_MARKER = 5;
|
|
36423
|
+
let skipKey;
|
|
36424
|
+
while (true) {
|
|
36425
|
+
if (ctx.req.signal?.aborted) {
|
|
36426
|
+
isPartial = true;
|
|
36427
|
+
break;
|
|
36428
|
+
}
|
|
36429
|
+
let response;
|
|
36430
|
+
try {
|
|
36431
|
+
response = await s3.send(new import_client_s33.ListObjectVersionsCommand({
|
|
36432
|
+
Bucket: containerName,
|
|
36433
|
+
MaxKeys: S3_MAX_KEYS_PER_REQUEST,
|
|
36434
|
+
KeyMarker: keyMarker,
|
|
36435
|
+
VersionIdMarker: versionIdMarker
|
|
36436
|
+
}), {
|
|
36437
|
+
abortSignal: ctx.req.signal
|
|
36438
|
+
});
|
|
36439
|
+
} catch (error) {
|
|
36440
|
+
if (ctx.req.signal?.aborted) {
|
|
36441
|
+
isPartial = true;
|
|
36442
|
+
break;
|
|
36443
|
+
}
|
|
36444
|
+
throw mapS3ErrorToTRPCError(error, {
|
|
36445
|
+
operation: "delete non-current versions",
|
|
36446
|
+
bucket: containerName
|
|
36447
|
+
});
|
|
36448
|
+
}
|
|
36449
|
+
const combined = [
|
|
36450
|
+
...(response.Versions ?? []).map((v) => ({
|
|
36451
|
+
Key: v.Key ?? "",
|
|
36452
|
+
VersionId: v.VersionId,
|
|
36453
|
+
IsLatest: v.IsLatest ?? false,
|
|
36454
|
+
isDeleteMarker: false
|
|
36455
|
+
})),
|
|
36456
|
+
...(response.DeleteMarkers ?? []).map((dm) => ({
|
|
36457
|
+
Key: dm.Key ?? "",
|
|
36458
|
+
VersionId: dm.VersionId,
|
|
36459
|
+
IsLatest: dm.IsLatest ?? false,
|
|
36460
|
+
isDeleteMarker: true
|
|
36461
|
+
}))
|
|
36462
|
+
].sort((a, b) => a.Key < b.Key ? -1 : a.Key > b.Key ? 1 : 0);
|
|
36463
|
+
const pageGroups = [];
|
|
36464
|
+
for (const item of combined) {
|
|
36465
|
+
const last = pageGroups[pageGroups.length - 1];
|
|
36466
|
+
if (last && last.key === item.Key) {
|
|
36467
|
+
last.items.push(item);
|
|
36468
|
+
} else {
|
|
36469
|
+
pageGroups.push({
|
|
36470
|
+
key: item.Key,
|
|
36471
|
+
items: [
|
|
36472
|
+
item
|
|
36473
|
+
]
|
|
36474
|
+
});
|
|
36475
|
+
}
|
|
36476
|
+
}
|
|
36477
|
+
if (pendingItems.length > 0) {
|
|
36478
|
+
if (pageGroups.length > 0 && pageGroups[0].key === pendingKey) {
|
|
36479
|
+
pageGroups[0].items = [
|
|
36480
|
+
...pendingItems,
|
|
36481
|
+
...pageGroups[0].items
|
|
36482
|
+
];
|
|
36483
|
+
} else {
|
|
36484
|
+
pageGroups.unshift({
|
|
36485
|
+
key: pendingKey,
|
|
36486
|
+
items: pendingItems
|
|
36487
|
+
});
|
|
36488
|
+
}
|
|
36489
|
+
pendingItems = [];
|
|
36490
|
+
pendingKey = void 0;
|
|
36491
|
+
}
|
|
36492
|
+
if (skipKey !== void 0) {
|
|
36493
|
+
const remaining = pageGroups.filter((group) => group.key !== skipKey);
|
|
36494
|
+
if (remaining.length === pageGroups.length) skipKey = void 0;
|
|
36495
|
+
pageGroups.length = 0;
|
|
36496
|
+
pageGroups.push(...remaining);
|
|
36497
|
+
}
|
|
36498
|
+
let groupsToProcess = pageGroups;
|
|
36499
|
+
if (response.IsTruncated && response.NextKeyMarker && pageGroups.length > 0) {
|
|
36500
|
+
const markerIndex = pageGroups.findIndex((group) => group.key === response.NextKeyMarker);
|
|
36501
|
+
const index = markerIndex >= 0 ? markerIndex : pageGroups.length - 1;
|
|
36502
|
+
const deferredGroup = pageGroups[index];
|
|
36503
|
+
groupsToProcess = pageGroups.filter((_, i) => i !== index);
|
|
36504
|
+
if (deferredGroup.items.length > S3_MAX_BUFFERED_VERSIONS_PER_KEY) {
|
|
36505
|
+
console.error(`[deleteNonCurrentVersions] Abandoning key with more than ${S3_MAX_BUFFERED_VERSIONS_PER_KEY} versions in bucket ${containerName}`);
|
|
36506
|
+
recordErrors([
|
|
36507
|
+
{
|
|
36508
|
+
key: deferredGroup.key,
|
|
36509
|
+
code: "TooManyVersions",
|
|
36510
|
+
message: `Key has more than ${S3_MAX_BUFFERED_VERSIONS_PER_KEY} versions and was skipped`
|
|
36511
|
+
}
|
|
36512
|
+
]);
|
|
36513
|
+
isPartial = true;
|
|
36514
|
+
skipKey = deferredGroup.key;
|
|
36515
|
+
} else {
|
|
36516
|
+
pendingKey = deferredGroup.key;
|
|
36517
|
+
pendingItems = deferredGroup.items;
|
|
36518
|
+
}
|
|
36519
|
+
}
|
|
36520
|
+
const itemsToDelete = [];
|
|
36521
|
+
for (const group of groupsToProcess) {
|
|
36522
|
+
const current = group.items.find((item) => item.IsLatest);
|
|
36523
|
+
if (current === void 0) {
|
|
36524
|
+
console.error(`[deleteNonCurrentVersions] Skipping key with no current version in bucket ${containerName}`);
|
|
36525
|
+
recordErrors([
|
|
36526
|
+
{
|
|
36527
|
+
key: group.key,
|
|
36528
|
+
code: "NoCurrentVersion",
|
|
36529
|
+
message: "No version flagged as current; skipped to avoid deleting the live object"
|
|
36530
|
+
}
|
|
36531
|
+
]);
|
|
36532
|
+
isPartial = true;
|
|
36533
|
+
continue;
|
|
36534
|
+
}
|
|
36535
|
+
const keepCurrentVersion = !current.isDeleteMarker;
|
|
36536
|
+
for (const item of group.items) {
|
|
36537
|
+
if (keepCurrentVersion && item === current) continue;
|
|
36538
|
+
if (!item.VersionId) {
|
|
36539
|
+
console.error(`[deleteNonCurrentVersions] Skipping item without VersionId in bucket ${containerName}`);
|
|
36540
|
+
recordErrors([
|
|
36541
|
+
{
|
|
36542
|
+
key: item.Key,
|
|
36543
|
+
code: "MissingVersionId",
|
|
36544
|
+
message: "Version record carried no VersionId; skipped to avoid creating a delete marker"
|
|
36545
|
+
}
|
|
36546
|
+
]);
|
|
36547
|
+
isPartial = true;
|
|
36548
|
+
continue;
|
|
36549
|
+
}
|
|
36550
|
+
itemsToDelete.push({
|
|
36551
|
+
key: item.Key,
|
|
36552
|
+
versionId: item.VersionId
|
|
36553
|
+
});
|
|
36554
|
+
}
|
|
36555
|
+
}
|
|
36556
|
+
if (itemsToDelete.length > 0) {
|
|
36557
|
+
const pageResult = await bulkDeleteItems(s3, containerName, itemsToDelete, ctx.req.signal, "delete non-current versions");
|
|
36558
|
+
deletedCount += pageResult.deletedCount;
|
|
36559
|
+
recordErrors(pageResult.errors);
|
|
36560
|
+
}
|
|
36561
|
+
if (!response.IsTruncated || !response.NextKeyMarker) {
|
|
36562
|
+
if (response.IsTruncated || ctx.req.signal?.aborted) isPartial = true;
|
|
36563
|
+
break;
|
|
36564
|
+
}
|
|
36565
|
+
const currentMarkerKey = `${keyMarker ?? ""}:${versionIdMarker ?? ""}`;
|
|
36566
|
+
const newMarkerKey = `${response.NextKeyMarker ?? ""}:${response.NextVersionIdMarker ?? ""}`;
|
|
36567
|
+
if (currentMarkerKey === newMarkerKey) {
|
|
36568
|
+
sameMarkerCount++;
|
|
36569
|
+
if (sameMarkerCount >= MAX_SAME_MARKER) {
|
|
36570
|
+
throw new import_server14.TRPCError({
|
|
36571
|
+
code: "INTERNAL_SERVER_ERROR",
|
|
36572
|
+
message: `Pagination stalled: markers not advancing after ${MAX_SAME_MARKER} iterations while deleting non-current versions from bucket ${containerName}.`
|
|
36573
|
+
});
|
|
36574
|
+
}
|
|
36575
|
+
} else {
|
|
36576
|
+
sameMarkerCount = 0;
|
|
36577
|
+
}
|
|
36578
|
+
keyMarker = response.NextKeyMarker;
|
|
36579
|
+
versionIdMarker = response.NextVersionIdMarker;
|
|
36580
|
+
}
|
|
36581
|
+
return deleteNonCurrentVersionsOutputSchema.parse({
|
|
36582
|
+
errors,
|
|
36583
|
+
deletedCount,
|
|
36584
|
+
errorCount,
|
|
36585
|
+
isPartial
|
|
36586
|
+
});
|
|
36587
|
+
}),
|
|
36588
|
+
/**
|
|
35869
36589
|
* Create a folder (zero-byte object with key ending in "/").
|
|
35870
36590
|
*
|
|
35871
36591
|
* In S3, folders are virtual - they're just zero-byte objects with keys ending in "/".
|
|
35872
|
-
*
|
|
36592
|
+
* The write is conditional (IfNoneMatch: "*"), so creating a folder that
|
|
36593
|
+
* already exists throws CONFLICT instead of silently overwriting it.
|
|
35873
36594
|
*
|
|
36595
|
+
* @throws TRPCError CONFLICT - a folder (or object) with this key already exists
|
|
35874
36596
|
* @throws TRPCError NOT_FOUND - bucket does not exist
|
|
35875
36597
|
* @throws TRPCError FORBIDDEN - no credentials or access denied
|
|
35876
36598
|
*/
|
|
@@ -35883,14 +36605,16 @@ var objectRouter = {
|
|
|
35883
36605
|
Bucket: containerName,
|
|
35884
36606
|
Key: normalizedPath,
|
|
35885
36607
|
Body: Buffer.from(""),
|
|
35886
|
-
ContentLength: 0
|
|
36608
|
+
ContentLength: 0,
|
|
36609
|
+
IfNoneMatch: "*"
|
|
35887
36610
|
}));
|
|
35888
36611
|
return true;
|
|
35889
36612
|
} catch (error) {
|
|
35890
36613
|
throw mapS3ErrorToTRPCError(error, {
|
|
35891
36614
|
operation: "create folder",
|
|
35892
36615
|
bucket: containerName,
|
|
35893
|
-
key: normalizedPath
|
|
36616
|
+
key: normalizedPath,
|
|
36617
|
+
preconditionFailedMeansAlreadyExists: true
|
|
35894
36618
|
});
|
|
35895
36619
|
}
|
|
35896
36620
|
}),
|
|
@@ -36630,7 +37354,6 @@ var ec2CredentialRouter = {
|
|
|
36630
37354
|
|
|
36631
37355
|
// src/server/Storage/routers/ceph/versioningRouter.ts
|
|
36632
37356
|
var import_client_s34 = require("@aws-sdk/client-s3");
|
|
36633
|
-
var import_zod17 = require("zod");
|
|
36634
37357
|
|
|
36635
37358
|
// src/server/Storage/types/versioning.ts
|
|
36636
37359
|
var import_zod16 = require("zod");
|
|
@@ -36703,6 +37426,72 @@ var listObjectVersionsInputSchema = projectScopedInputSchema.extend({
|
|
|
36703
37426
|
bucket: import_zod16.z.string().min(1),
|
|
36704
37427
|
key: import_zod16.z.string().min(1)
|
|
36705
37428
|
});
|
|
37429
|
+
var checkDeletedContentInputSchema = projectScopedInputSchema.extend({
|
|
37430
|
+
bucket: import_zod16.z.string().min(1),
|
|
37431
|
+
prefix: import_zod16.z.string().optional(),
|
|
37432
|
+
folders: import_zod16.z.array(import_zod16.z.string()).max(1e3).optional()
|
|
37433
|
+
}).refine((input) => input.prefix !== void 0 || (input.folders?.length ?? 0) > 0, {
|
|
37434
|
+
message: "Either prefix or a non-empty folders array is required",
|
|
37435
|
+
path: [
|
|
37436
|
+
"prefix"
|
|
37437
|
+
]
|
|
37438
|
+
}).refine((input) => input.folders?.every((folder) => folder.startsWith(input.prefix ?? "")) ?? true, {
|
|
37439
|
+
message: "Every folder must live under prefix",
|
|
37440
|
+
path: [
|
|
37441
|
+
"folders"
|
|
37442
|
+
]
|
|
37443
|
+
});
|
|
37444
|
+
var checkDeletedContentOutputSchema = import_zod16.z.array(import_zod16.z.object({
|
|
37445
|
+
prefix: import_zod16.z.string(),
|
|
37446
|
+
hasDeletedContent: import_zod16.z.boolean(),
|
|
37447
|
+
isFolderDeleted: import_zod16.z.boolean(),
|
|
37448
|
+
folderDeleteMarkerVersionId: import_zod16.z.string().optional(),
|
|
37449
|
+
folderMarkerVersionId: import_zod16.z.string().optional(),
|
|
37450
|
+
isPartialScan: import_zod16.z.boolean()
|
|
37451
|
+
}));
|
|
37452
|
+
|
|
37453
|
+
// src/server/Storage/helpers/versionScan.ts
|
|
37454
|
+
function folderPrefixOf(key, prefix) {
|
|
37455
|
+
if (!key.startsWith(prefix)) return void 0;
|
|
37456
|
+
const rest = key.slice(prefix.length);
|
|
37457
|
+
const i = rest.indexOf("/");
|
|
37458
|
+
return i === -1 ? void 0 : prefix + rest.slice(0, i + 1);
|
|
37459
|
+
}
|
|
37460
|
+
__name(folderPrefixOf, "folderPrefixOf");
|
|
37461
|
+
function isFolderCovered(folderPrefix, stoppedAtKey) {
|
|
37462
|
+
return stoppedAtKey === void 0 ? true : folderPrefix < stoppedAtKey && !stoppedAtKey.startsWith(folderPrefix);
|
|
37463
|
+
}
|
|
37464
|
+
__name(isFolderCovered, "isFolderCovered");
|
|
37465
|
+
function longestCommonPrefix(folders) {
|
|
37466
|
+
if (folders.length === 0) return "";
|
|
37467
|
+
let common = folders[0];
|
|
37468
|
+
for (let i = 1; i < folders.length && common.length > 0; i++) {
|
|
37469
|
+
const folder = folders[i];
|
|
37470
|
+
let j = 0;
|
|
37471
|
+
while (j < common.length && j < folder.length && common[j] === folder[j]) j++;
|
|
37472
|
+
common = common.slice(0, j);
|
|
37473
|
+
}
|
|
37474
|
+
if (common.endsWith("/") && folders.some((folder) => folder === common)) {
|
|
37475
|
+
common = common.slice(0, -1);
|
|
37476
|
+
}
|
|
37477
|
+
const lastSlash = common.lastIndexOf("/");
|
|
37478
|
+
return lastSlash === -1 ? "" : common.slice(0, lastSlash + 1);
|
|
37479
|
+
}
|
|
37480
|
+
__name(longestCommonPrefix, "longestCommonPrefix");
|
|
37481
|
+
function greatestKeyOnPage(versions, deleteMarkers) {
|
|
37482
|
+
let greatest;
|
|
37483
|
+
for (const entries of [
|
|
37484
|
+
versions,
|
|
37485
|
+
deleteMarkers
|
|
37486
|
+
]) {
|
|
37487
|
+
for (const entry of entries ?? []) {
|
|
37488
|
+
const key = entry.Key;
|
|
37489
|
+
if (key !== void 0 && (greatest === void 0 || key > greatest)) greatest = key;
|
|
37490
|
+
}
|
|
37491
|
+
}
|
|
37492
|
+
return greatest;
|
|
37493
|
+
}
|
|
37494
|
+
__name(greatestKeyOnPage, "greatestKeyOnPage");
|
|
36706
37495
|
|
|
36707
37496
|
// src/server/Storage/routers/ceph/versioningRouter.ts
|
|
36708
37497
|
var versioningRouter = {
|
|
@@ -36948,104 +37737,144 @@ var versioningRouter = {
|
|
|
36948
37737
|
/**
|
|
36949
37738
|
* Check if folders contain deleted files (files with delete markers) or if the folder marker itself is deleted.
|
|
36950
37739
|
*
|
|
36951
|
-
*
|
|
36952
|
-
*
|
|
36953
|
-
*
|
|
37740
|
+
* Performs a single paginated, delimiter-less scan of the parent prefix (either the
|
|
37741
|
+
* caller-supplied `prefix`, or — for backward compatibility — the longest common prefix of
|
|
37742
|
+
* `folders`) and attributes every key it sees to its direct-child folder. This replaces the
|
|
37743
|
+
* previous design of one unbounded scan per folder: the union of what N per-folder scans
|
|
37744
|
+
* would return is exactly what one scan of their common parent returns, minus "loose" objects
|
|
37745
|
+
* that live directly under the parent (which aren't attributed to any folder and are ignored).
|
|
36954
37746
|
*
|
|
36955
|
-
*
|
|
36956
|
-
*
|
|
37747
|
+
* Cost: at most `S3_MAX_SCAN_PAGES` requests total, independent of how many folders are being
|
|
37748
|
+
* checked (as opposed to the old N-scans-for-N-folders behaviour).
|
|
37749
|
+
*
|
|
37750
|
+
* Honours `ctx.req.signal`: if the caller navigates away mid-scan, the loop stops and returns
|
|
37751
|
+
* whatever it has accumulated so far, flagged via `isPartialScan`.
|
|
37752
|
+
*
|
|
37753
|
+
* `isPartialScan` (per folder) is `true` when the scan hit the page ceiling (or was aborted)
|
|
37754
|
+
* before fully covering that folder's key range. It is the caller's job to treat it as
|
|
37755
|
+
* "unknown", not as "confirmed empty": `hasDeletedContent: false` together with
|
|
37756
|
+
* `isPartialScan: true` means "not fully checked", not "clean". A `true` value for
|
|
37757
|
+
* `hasDeletedContent` or `isFolderDeleted` is always reliable, partial scan or not — an
|
|
37758
|
+
* incomplete scan can only miss a delete marker, never invent one.
|
|
36957
37759
|
*
|
|
36958
37760
|
* @throws TRPCError NOT_FOUND - bucket does not exist
|
|
36959
37761
|
* @throws TRPCError FORBIDDEN - no credentials or access denied
|
|
36960
37762
|
*/
|
|
36961
|
-
checkDeletedContent: cephProtectedProcedure.input(
|
|
36962
|
-
project_id: import_zod17.z.string(),
|
|
36963
|
-
bucket: import_zod17.z.string().min(1),
|
|
36964
|
-
folders: import_zod17.z.array(import_zod17.z.string()).max(100)
|
|
36965
|
-
})).query(async ({ ctx, input }) => {
|
|
37763
|
+
checkDeletedContent: cephProtectedProcedure.input(checkDeletedContentInputSchema).query(async ({ ctx, input }) => {
|
|
36966
37764
|
const s3 = ctx.getCephClient();
|
|
36967
|
-
|
|
36968
|
-
|
|
36969
|
-
|
|
36970
|
-
|
|
36971
|
-
|
|
36972
|
-
|
|
36973
|
-
|
|
36974
|
-
|
|
36975
|
-
|
|
36976
|
-
|
|
36977
|
-
|
|
36978
|
-
|
|
36979
|
-
|
|
36980
|
-
|
|
36981
|
-
|
|
36982
|
-
|
|
36983
|
-
|
|
36984
|
-
|
|
36985
|
-
|
|
36986
|
-
|
|
36987
|
-
|
|
36988
|
-
|
|
36989
|
-
|
|
36990
|
-
|
|
36991
|
-
|
|
36992
|
-
|
|
36993
|
-
|
|
36994
|
-
|
|
36995
|
-
|
|
36996
|
-
|
|
36997
|
-
|
|
36998
|
-
|
|
36999
|
-
|
|
37000
|
-
|
|
37001
|
-
|
|
37002
|
-
|
|
37003
|
-
|
|
37004
|
-
}
|
|
37005
|
-
}
|
|
37006
|
-
}
|
|
37007
|
-
if (!hasDeletedNestedObjects) {
|
|
37008
|
-
const deleteMarkersExcludingFolder = response.DeleteMarkers?.filter((dm) => dm.Key !== folderPrefix && dm.IsLatest === true) ?? [];
|
|
37009
|
-
hasDeletedNestedObjects = deleteMarkersExcludingFolder.length > 0;
|
|
37010
|
-
}
|
|
37011
|
-
if (hasDeletedNestedObjects && isFolderMarkerDeleted && folderMarkerVersionId) {
|
|
37012
|
-
break;
|
|
37013
|
-
}
|
|
37014
|
-
if (!response.IsTruncated || !response.NextKeyMarker) {
|
|
37015
|
-
break;
|
|
37016
|
-
}
|
|
37017
|
-
keyMarker = response.NextKeyMarker;
|
|
37018
|
-
versionIdMarker = response.NextVersionIdMarker;
|
|
37019
|
-
} while (true);
|
|
37020
|
-
return {
|
|
37021
|
-
prefix: folderPrefix,
|
|
37022
|
-
hasDeletedContent: isFolderMarkerDeleted || hasDeletedNestedObjects,
|
|
37023
|
-
isFolderDeleted: isFolderMarkerDeleted,
|
|
37024
|
-
folderDeleteMarkerVersionId,
|
|
37025
|
-
folderMarkerVersionId
|
|
37026
|
-
};
|
|
37027
|
-
} catch {
|
|
37028
|
-
return {
|
|
37029
|
-
prefix: folderPrefix,
|
|
37030
|
-
hasDeletedContent: false,
|
|
37031
|
-
isFolderDeleted: false
|
|
37032
|
-
};
|
|
37765
|
+
const scanPrefix = input.prefix ?? longestCommonPrefix(input.folders ?? []);
|
|
37766
|
+
const acc = /* @__PURE__ */ new Map();
|
|
37767
|
+
const getOrCreate = /* @__PURE__ */ __name((folder) => {
|
|
37768
|
+
let entry = acc.get(folder);
|
|
37769
|
+
if (!entry) {
|
|
37770
|
+
entry = {
|
|
37771
|
+
hasDeletedNested: false,
|
|
37772
|
+
isFolderDeleted: false
|
|
37773
|
+
};
|
|
37774
|
+
acc.set(folder, entry);
|
|
37775
|
+
}
|
|
37776
|
+
return entry;
|
|
37777
|
+
}, "getOrCreate");
|
|
37778
|
+
let keyMarker;
|
|
37779
|
+
let versionIdMarker;
|
|
37780
|
+
let pages = 0;
|
|
37781
|
+
let stoppedAtKey;
|
|
37782
|
+
while (true) {
|
|
37783
|
+
if (ctx.req.signal?.aborted) {
|
|
37784
|
+
stoppedAtKey = keyMarker ?? "";
|
|
37785
|
+
break;
|
|
37786
|
+
}
|
|
37787
|
+
let response;
|
|
37788
|
+
try {
|
|
37789
|
+
response = await s3.send(new import_client_s34.ListObjectVersionsCommand({
|
|
37790
|
+
Bucket: input.bucket,
|
|
37791
|
+
Prefix: scanPrefix || void 0,
|
|
37792
|
+
MaxKeys: S3_MAX_KEYS_PER_REQUEST,
|
|
37793
|
+
KeyMarker: keyMarker,
|
|
37794
|
+
VersionIdMarker: versionIdMarker
|
|
37795
|
+
}), {
|
|
37796
|
+
abortSignal: ctx.req.signal
|
|
37797
|
+
});
|
|
37798
|
+
} catch (error) {
|
|
37799
|
+
if (ctx.req.signal?.aborted) {
|
|
37800
|
+
stoppedAtKey = keyMarker ?? "";
|
|
37801
|
+
break;
|
|
37033
37802
|
}
|
|
37034
|
-
|
|
37035
|
-
|
|
37036
|
-
|
|
37037
|
-
|
|
37038
|
-
|
|
37039
|
-
|
|
37040
|
-
|
|
37803
|
+
console.error("checkDeletedContent scan failed", {
|
|
37804
|
+
bucket: input.bucket,
|
|
37805
|
+
prefix: scanPrefix,
|
|
37806
|
+
pages,
|
|
37807
|
+
error
|
|
37808
|
+
});
|
|
37809
|
+
throw mapS3ErrorToTRPCError(error, {
|
|
37810
|
+
operation: "check deleted content",
|
|
37811
|
+
bucket: input.bucket
|
|
37812
|
+
});
|
|
37813
|
+
}
|
|
37814
|
+
for (const v of response.Versions ?? []) {
|
|
37815
|
+
const key = v.Key ?? "";
|
|
37816
|
+
const folder = folderPrefixOf(key, scanPrefix);
|
|
37817
|
+
if (!folder) continue;
|
|
37818
|
+
if (key === folder) {
|
|
37819
|
+
const entry = getOrCreate(folder);
|
|
37820
|
+
const lastModified = v.LastModified?.getTime() ?? 0;
|
|
37821
|
+
if (entry.folderMarkerVersionId === void 0 || lastModified > (entry.folderMarkerLastModified ?? -1)) {
|
|
37822
|
+
entry.folderMarkerVersionId = v.VersionId;
|
|
37823
|
+
entry.folderMarkerLastModified = lastModified;
|
|
37824
|
+
}
|
|
37825
|
+
}
|
|
37826
|
+
}
|
|
37827
|
+
for (const dm of response.DeleteMarkers ?? []) {
|
|
37828
|
+
const key = dm.Key ?? "";
|
|
37829
|
+
const folder = folderPrefixOf(key, scanPrefix);
|
|
37830
|
+
if (!folder) continue;
|
|
37831
|
+
const entry = getOrCreate(folder);
|
|
37832
|
+
if (key === folder) {
|
|
37833
|
+
if (dm.IsLatest) {
|
|
37834
|
+
entry.isFolderDeleted = true;
|
|
37835
|
+
entry.folderDeleteMarkerVersionId = dm.VersionId;
|
|
37836
|
+
}
|
|
37837
|
+
} else if (dm.IsLatest === true) {
|
|
37838
|
+
entry.hasDeletedNested = true;
|
|
37839
|
+
}
|
|
37840
|
+
}
|
|
37841
|
+
pages++;
|
|
37842
|
+
if (!response.IsTruncated) {
|
|
37843
|
+
stoppedAtKey = void 0;
|
|
37844
|
+
break;
|
|
37845
|
+
}
|
|
37846
|
+
if (!response.NextKeyMarker) {
|
|
37847
|
+
stoppedAtKey = greatestKeyOnPage(response.Versions, response.DeleteMarkers) ?? keyMarker ?? "";
|
|
37848
|
+
break;
|
|
37849
|
+
}
|
|
37850
|
+
if (pages >= S3_MAX_SCAN_PAGES) {
|
|
37851
|
+
stoppedAtKey = response.NextKeyMarker;
|
|
37852
|
+
break;
|
|
37853
|
+
}
|
|
37854
|
+
keyMarker = response.NextKeyMarker;
|
|
37855
|
+
versionIdMarker = response.NextVersionIdMarker;
|
|
37041
37856
|
}
|
|
37857
|
+
const folders = input.folders ?? [
|
|
37858
|
+
...acc.keys()
|
|
37859
|
+
];
|
|
37860
|
+
return checkDeletedContentOutputSchema.parse(folders.map((prefix) => {
|
|
37861
|
+
const entry = acc.get(prefix);
|
|
37862
|
+
return {
|
|
37863
|
+
prefix,
|
|
37864
|
+
hasDeletedContent: (entry?.isFolderDeleted ?? false) || (entry?.hasDeletedNested ?? false),
|
|
37865
|
+
isFolderDeleted: entry?.isFolderDeleted ?? false,
|
|
37866
|
+
folderDeleteMarkerVersionId: entry?.folderDeleteMarkerVersionId,
|
|
37867
|
+
folderMarkerVersionId: entry?.folderMarkerVersionId,
|
|
37868
|
+
isPartialScan: !isFolderCovered(prefix, stoppedAtKey)
|
|
37869
|
+
};
|
|
37870
|
+
}));
|
|
37042
37871
|
})
|
|
37043
37872
|
};
|
|
37044
37873
|
|
|
37045
37874
|
// src/server/Storage/routers/ceph/bucketPolicyRouter.ts
|
|
37046
37875
|
var import_client_s35 = require("@aws-sdk/client-s3");
|
|
37047
37876
|
var import_server16 = require("@trpc/server");
|
|
37048
|
-
var
|
|
37877
|
+
var import_zod17 = require("zod");
|
|
37049
37878
|
var policySetRateLimits = /* @__PURE__ */ new Map();
|
|
37050
37879
|
function checkPolicySetRateLimit(bucketName, projectId) {
|
|
37051
37880
|
const key = `${projectId}:${bucketName}`;
|
|
@@ -37081,12 +37910,12 @@ function validateResourceARNsMatchBucket(policy, bucketName) {
|
|
|
37081
37910
|
statement.Resource
|
|
37082
37911
|
];
|
|
37083
37912
|
for (const resource of resources) {
|
|
37084
|
-
const arnRegex = /^arn:aws:s3
|
|
37913
|
+
const arnRegex = /^arn:aws:s3::[^:/]*:([^/]+)/;
|
|
37085
37914
|
const match = resource.match(arnRegex);
|
|
37086
37915
|
if (!match) {
|
|
37087
37916
|
throw new import_server16.TRPCError({
|
|
37088
37917
|
code: "BAD_REQUEST",
|
|
37089
|
-
message: `Policy Resource '${resource}' is not a valid S3 bucket ARN for bucket '${bucketName}'. Expected: arn:aws:s3:::${bucketName} or arn:aws:s3
|
|
37918
|
+
message: `Policy Resource '${resource}' is not a valid S3 bucket ARN for bucket '${bucketName}'. Expected: arn:aws:s3:::${bucketName}, arn:aws:s3:::${bucketName}/* or arn:aws:s3::TENANT:${bucketName}`
|
|
37090
37919
|
});
|
|
37091
37920
|
}
|
|
37092
37921
|
if (match[1] !== bucketName) {
|
|
@@ -37121,12 +37950,22 @@ function validatePolicySemantics(policy) {
|
|
|
37121
37950
|
}
|
|
37122
37951
|
}
|
|
37123
37952
|
__name(validatePolicySemantics, "validatePolicySemantics");
|
|
37953
|
+
function parseStoredPolicy(policyText) {
|
|
37954
|
+
try {
|
|
37955
|
+
const result = bucketPolicyDocumentSchema.safeParse(JSON.parse(policyText));
|
|
37956
|
+
return result.success ? result.data : null;
|
|
37957
|
+
} catch {
|
|
37958
|
+
return null;
|
|
37959
|
+
}
|
|
37960
|
+
}
|
|
37961
|
+
__name(parseStoredPolicy, "parseStoredPolicy");
|
|
37124
37962
|
var bucketPolicyRouter = {
|
|
37125
37963
|
/**
|
|
37126
37964
|
* Get the current bucket policy.
|
|
37127
37965
|
*
|
|
37128
|
-
* Returns the policy as both a parsed object and raw JSON string.
|
|
37129
|
-
*
|
|
37966
|
+
* Returns the policy as both a parsed object and raw JSON string. `policyText` is always the
|
|
37967
|
+
* document exactly as RGW stored it; `policy` is null when no policy is set, and also when the
|
|
37968
|
+
* stored document doesn't fit our schema — the editor shows the raw text either way.
|
|
37130
37969
|
*
|
|
37131
37970
|
* @throws TRPCError NOT_FOUND - bucket does not exist
|
|
37132
37971
|
* @throws TRPCError FORBIDDEN - no credentials or access denied
|
|
@@ -37145,10 +37984,8 @@ var bucketPolicyRouter = {
|
|
|
37145
37984
|
policyText: null
|
|
37146
37985
|
};
|
|
37147
37986
|
}
|
|
37148
|
-
const policyObj = JSON.parse(policyText);
|
|
37149
|
-
const policy = bucketPolicyDocumentSchema.parse(policyObj);
|
|
37150
37987
|
return {
|
|
37151
|
-
policy,
|
|
37988
|
+
policy: parseStoredPolicy(policyText),
|
|
37152
37989
|
policyText
|
|
37153
37990
|
};
|
|
37154
37991
|
} catch (error) {
|
|
@@ -37210,7 +38047,7 @@ var bucketPolicyRouter = {
|
|
|
37210
38047
|
message: "Invalid JSON format"
|
|
37211
38048
|
});
|
|
37212
38049
|
}
|
|
37213
|
-
if (error instanceof
|
|
38050
|
+
if (error instanceof import_zod17.z.ZodError) {
|
|
37214
38051
|
const zodError = error;
|
|
37215
38052
|
const firstError = zodError.issues?.[0];
|
|
37216
38053
|
if (!firstError) {
|
|
@@ -37857,40 +38694,40 @@ var buildObjectStorageRouters = /* @__PURE__ */ __name((policyDir) => ({
|
|
|
37857
38694
|
}), "buildObjectStorageRouters");
|
|
37858
38695
|
|
|
37859
38696
|
// src/server/Project/routers/projectRouter.ts
|
|
37860
|
-
var
|
|
38697
|
+
var import_zod19 = require("zod");
|
|
37861
38698
|
var import_server19 = require("@trpc/server");
|
|
37862
38699
|
|
|
37863
38700
|
// src/server/Project/types/models.ts
|
|
37864
|
-
var
|
|
37865
|
-
var baseProjectSchema =
|
|
37866
|
-
id:
|
|
37867
|
-
name:
|
|
37868
|
-
enabled:
|
|
37869
|
-
domain_id:
|
|
37870
|
-
parent_id:
|
|
37871
|
-
is_domain:
|
|
37872
|
-
tags:
|
|
37873
|
-
options:
|
|
37874
|
-
description:
|
|
37875
|
-
links:
|
|
37876
|
-
self:
|
|
38701
|
+
var import_zod18 = require("zod");
|
|
38702
|
+
var baseProjectSchema = import_zod18.z.object({
|
|
38703
|
+
id: import_zod18.z.string(),
|
|
38704
|
+
name: import_zod18.z.string(),
|
|
38705
|
+
enabled: import_zod18.z.boolean(),
|
|
38706
|
+
domain_id: import_zod18.z.string().nullable().optional(),
|
|
38707
|
+
parent_id: import_zod18.z.string().nullable().optional(),
|
|
38708
|
+
is_domain: import_zod18.z.boolean().nullable().optional(),
|
|
38709
|
+
tags: import_zod18.z.array(import_zod18.z.string()).nullable().optional(),
|
|
38710
|
+
options: import_zod18.z.record(import_zod18.z.string(), import_zod18.z.string().or(import_zod18.z.boolean())).nullable().optional(),
|
|
38711
|
+
description: import_zod18.z.string().nullable().optional(),
|
|
38712
|
+
links: import_zod18.z.object({
|
|
38713
|
+
self: import_zod18.z.string().url().nullable().optional()
|
|
37877
38714
|
}).optional()
|
|
37878
38715
|
});
|
|
37879
38716
|
var projectSchema = baseProjectSchema.extend({
|
|
37880
|
-
domain_name:
|
|
37881
|
-
parents:
|
|
38717
|
+
domain_name: import_zod18.z.string().optional(),
|
|
38718
|
+
parents: import_zod18.z.array(import_zod18.z.object({
|
|
37882
38719
|
project: baseProjectSchema
|
|
37883
38720
|
})).optional()
|
|
37884
38721
|
});
|
|
37885
|
-
var projectResponseSchema =
|
|
38722
|
+
var projectResponseSchema = import_zod18.z.object({
|
|
37886
38723
|
project: projectSchema
|
|
37887
38724
|
});
|
|
37888
|
-
var projectsResponseSchema =
|
|
37889
|
-
projects:
|
|
37890
|
-
links:
|
|
37891
|
-
self:
|
|
37892
|
-
previous:
|
|
37893
|
-
next:
|
|
38725
|
+
var projectsResponseSchema = import_zod18.z.object({
|
|
38726
|
+
projects: import_zod18.z.array(projectSchema),
|
|
38727
|
+
links: import_zod18.z.object({
|
|
38728
|
+
self: import_zod18.z.string().url().optional(),
|
|
38729
|
+
previous: import_zod18.z.string().url().nullable().optional(),
|
|
38730
|
+
next: import_zod18.z.string().url().nullable().optional()
|
|
37894
38731
|
}).optional()
|
|
37895
38732
|
});
|
|
37896
38733
|
|
|
@@ -37977,10 +38814,10 @@ var projectRouter = {
|
|
|
37977
38814
|
return void 0;
|
|
37978
38815
|
}
|
|
37979
38816
|
const domainsData = domainsResponse ? await domainsResponse.json().catch(() => null) : null;
|
|
37980
|
-
const domainsResponseSchema =
|
|
37981
|
-
domains:
|
|
37982
|
-
id:
|
|
37983
|
-
name:
|
|
38817
|
+
const domainsResponseSchema = import_zod19.z.object({
|
|
38818
|
+
domains: import_zod19.z.array(import_zod19.z.object({
|
|
38819
|
+
id: import_zod19.z.string(),
|
|
38820
|
+
name: import_zod19.z.string()
|
|
37984
38821
|
}))
|
|
37985
38822
|
});
|
|
37986
38823
|
const parsedDomains = domainsResponseSchema.safeParse(domainsData);
|
|
@@ -38000,8 +38837,8 @@ var projectRouter = {
|
|
|
38000
38837
|
* filtered by the optional search term. Uses the OpenStack /v3/auth/projects
|
|
38001
38838
|
* endpoint which works with any valid token.
|
|
38002
38839
|
*/
|
|
38003
|
-
searchProjects: protectedProcedure.input(
|
|
38004
|
-
search:
|
|
38840
|
+
searchProjects: protectedProcedure.input(import_zod19.z.object({
|
|
38841
|
+
search: import_zod19.z.string().optional()
|
|
38005
38842
|
}).optional()).query(async ({ ctx, input }) => {
|
|
38006
38843
|
if (!ctx.openstack) {
|
|
38007
38844
|
throw new import_server19.TRPCError({
|
|
@@ -38031,8 +38868,65 @@ var projectRouter = {
|
|
|
38031
38868
|
projects.sort((a, b) => a.name.localeCompare(b.name));
|
|
38032
38869
|
return projects;
|
|
38033
38870
|
}),
|
|
38034
|
-
|
|
38035
|
-
|
|
38871
|
+
/**
|
|
38872
|
+
* List projects with unified search functionality
|
|
38873
|
+
*
|
|
38874
|
+
* Returns all projects that the authenticated user has access to,
|
|
38875
|
+
* filtered by ID, name, and description using the standard filterBySearchParams helper.
|
|
38876
|
+
* This endpoint provides consistent search behavior with other resources in the system.
|
|
38877
|
+
*/
|
|
38878
|
+
listProjectsWithSearch: protectedProcedure.input(import_zod19.z.object({
|
|
38879
|
+
searchTerm: import_zod19.z.string().optional()
|
|
38880
|
+
})).query(async ({ ctx, input }) => {
|
|
38881
|
+
if (!ctx.openstack) {
|
|
38882
|
+
throw new import_server19.TRPCError({
|
|
38883
|
+
code: "UNAUTHORIZED",
|
|
38884
|
+
message: "No authenticated session"
|
|
38885
|
+
});
|
|
38886
|
+
}
|
|
38887
|
+
const token = ctx.openstack.getToken();
|
|
38888
|
+
if (!token?.authToken) {
|
|
38889
|
+
throw new import_server19.TRPCError({
|
|
38890
|
+
code: "UNAUTHORIZED",
|
|
38891
|
+
message: "No auth token available"
|
|
38892
|
+
});
|
|
38893
|
+
}
|
|
38894
|
+
const [response, domainsResponse] = await Promise.all([
|
|
38895
|
+
callIdentityAPI(ctx.identityEndpoint, token.authToken, "auth/projects"),
|
|
38896
|
+
callIdentityAPI(ctx.identityEndpoint, token.authToken, "auth/domains").catch(() => null)
|
|
38897
|
+
]);
|
|
38898
|
+
const data = await response.json();
|
|
38899
|
+
const parsedData = projectsResponseSchema.safeParse(data);
|
|
38900
|
+
if (!parsedData.success) {
|
|
38901
|
+
console.error("Zod Parsing Error:", parsedData.error.message);
|
|
38902
|
+
return void 0;
|
|
38903
|
+
}
|
|
38904
|
+
const domainsData = domainsResponse ? await domainsResponse.json().catch(() => null) : null;
|
|
38905
|
+
const domainsResponseSchema = import_zod19.z.object({
|
|
38906
|
+
domains: import_zod19.z.array(import_zod19.z.object({
|
|
38907
|
+
id: import_zod19.z.string(),
|
|
38908
|
+
name: import_zod19.z.string()
|
|
38909
|
+
}))
|
|
38910
|
+
});
|
|
38911
|
+
const parsedDomains = domainsResponseSchema.safeParse(domainsData);
|
|
38912
|
+
const domainMap = new Map((parsedDomains.success ? parsedDomains.data.domains : []).map((d) => [
|
|
38913
|
+
d.id,
|
|
38914
|
+
d.name
|
|
38915
|
+
]));
|
|
38916
|
+
const projectsWithDomain = parsedData.data.projects.map((project) => ({
|
|
38917
|
+
...project,
|
|
38918
|
+
domain_name: project.domain_id ? domainMap.get(project.domain_id) ?? void 0 : void 0
|
|
38919
|
+
}));
|
|
38920
|
+
return filterBySearchParams(projectsWithDomain, input.searchTerm, [
|
|
38921
|
+
"id",
|
|
38922
|
+
"name",
|
|
38923
|
+
"description",
|
|
38924
|
+
"domain_name",
|
|
38925
|
+
"domain_id"
|
|
38926
|
+
]);
|
|
38927
|
+
}),
|
|
38928
|
+
getProject: protectedProcedure.input(import_zod19.z.object({
|
|
38929
|
+
projectId: import_zod19.z.string()
|
|
38036
38930
|
})).query(async ({ ctx, input }) => {
|
|
38037
38931
|
if (!ctx.openstack) {
|
|
38038
38932
|
throw new import_server19.TRPCError({
|
|
@@ -38142,16 +39036,16 @@ __name(validateOpenstackService, "validateOpenstackService");
|
|
|
38142
39036
|
var import_signal_openstack7 = __toESM(require_esm());
|
|
38143
39037
|
|
|
38144
39038
|
// src/server/Network/types/floatingIp.ts
|
|
38145
|
-
var
|
|
39039
|
+
var import_zod21 = require("zod");
|
|
38146
39040
|
|
|
38147
39041
|
// src/server/Network/types/index.ts
|
|
38148
|
-
var
|
|
38149
|
-
var ISO8601TimestampSchema =
|
|
38150
|
-
var SortDirSchema =
|
|
39042
|
+
var import_zod20 = require("zod");
|
|
39043
|
+
var ISO8601TimestampSchema = import_zod20.z.string().brand("ISO8601Timestamp");
|
|
39044
|
+
var SortDirSchema = import_zod20.z.enum([
|
|
38151
39045
|
"asc",
|
|
38152
39046
|
"desc"
|
|
38153
39047
|
]);
|
|
38154
|
-
var NetworkPortStatusSchema =
|
|
39048
|
+
var NetworkPortStatusSchema = import_zod20.z.enum([
|
|
38155
39049
|
"ACTIVE",
|
|
38156
39050
|
"DOWN",
|
|
38157
39051
|
"BUILD",
|
|
@@ -38159,153 +39053,153 @@ var NetworkPortStatusSchema = import_zod21.z.enum([
|
|
|
38159
39053
|
]);
|
|
38160
39054
|
|
|
38161
39055
|
// src/server/Network/types/floatingIp.ts
|
|
38162
|
-
var FloatingIpStatusSchema =
|
|
39056
|
+
var FloatingIpStatusSchema = import_zod21.z.enum([
|
|
38163
39057
|
"ACTIVE",
|
|
38164
39058
|
"DOWN",
|
|
38165
39059
|
"ERROR"
|
|
38166
39060
|
]);
|
|
38167
|
-
var PortDetailsSchema =
|
|
39061
|
+
var PortDetailsSchema = import_zod21.z.object({
|
|
38168
39062
|
/** The status of the port */
|
|
38169
39063
|
status: FloatingIpStatusSchema,
|
|
38170
39064
|
/** The name of the port */
|
|
38171
|
-
name:
|
|
39065
|
+
name: import_zod21.z.string(),
|
|
38172
39066
|
/** Administrative state of the port (true = UP, false = DOWN) */
|
|
38173
|
-
admin_state_up:
|
|
39067
|
+
admin_state_up: import_zod21.z.boolean(),
|
|
38174
39068
|
/** The ID of the network the port is attached to */
|
|
38175
|
-
network_id:
|
|
39069
|
+
network_id: import_zod21.z.string(),
|
|
38176
39070
|
/** The owner of the port device */
|
|
38177
|
-
device_owner:
|
|
39071
|
+
device_owner: import_zod21.z.string(),
|
|
38178
39072
|
/** The MAC address of the port */
|
|
38179
|
-
mac_address:
|
|
39073
|
+
mac_address: import_zod21.z.string(),
|
|
38180
39074
|
/** The ID of the device the port is attached to */
|
|
38181
|
-
device_id:
|
|
39075
|
+
device_id: import_zod21.z.string()
|
|
38182
39076
|
});
|
|
38183
|
-
var PortForwardingSchema =
|
|
39077
|
+
var PortForwardingSchema = import_zod21.z.object({
|
|
38184
39078
|
/** Port forwarding protocol */
|
|
38185
|
-
protocol:
|
|
39079
|
+
protocol: import_zod21.z.enum([
|
|
38186
39080
|
"tcp",
|
|
38187
39081
|
"udp"
|
|
38188
39082
|
]),
|
|
38189
39083
|
/** The internal fixed IP address associated with the port forwarding */
|
|
38190
|
-
internal_ip_address:
|
|
39084
|
+
internal_ip_address: import_zod21.z.string(),
|
|
38191
39085
|
/** The internal port number or starting port of the range */
|
|
38192
|
-
internal_port:
|
|
39086
|
+
internal_port: import_zod21.z.number().optional(),
|
|
38193
39087
|
/** The internal port range (e.g., "1024:2048") */
|
|
38194
|
-
internal_port_range:
|
|
39088
|
+
internal_port_range: import_zod21.z.string().optional(),
|
|
38195
39089
|
/** The internal port ID for the port forwarding (requires floating-ip-port-forwarding-detail extension) */
|
|
38196
|
-
internal_port_id:
|
|
39090
|
+
internal_port_id: import_zod21.z.string().optional(),
|
|
38197
39091
|
/** The external port number or starting port of the range */
|
|
38198
|
-
external_port:
|
|
39092
|
+
external_port: import_zod21.z.number().optional(),
|
|
38199
39093
|
/** The external port range (e.g., "8000:9000") */
|
|
38200
|
-
external_port_range:
|
|
39094
|
+
external_port_range: import_zod21.z.string().optional(),
|
|
38201
39095
|
/** The ID of the port forwarding resource */
|
|
38202
|
-
id:
|
|
39096
|
+
id: import_zod21.z.string(),
|
|
38203
39097
|
/** Description of the port forwarding rule (requires floating-ip-port-forwarding-description extension) */
|
|
38204
|
-
description:
|
|
39098
|
+
description: import_zod21.z.string().nullable().optional()
|
|
38205
39099
|
});
|
|
38206
|
-
var FloatingIpSchema =
|
|
39100
|
+
var FloatingIpSchema = import_zod21.z.object({
|
|
38207
39101
|
/** The ID of the router for the floating IP (null if not associated) */
|
|
38208
|
-
router_id:
|
|
39102
|
+
router_id: import_zod21.z.string().nullable(),
|
|
38209
39103
|
/** A human-readable description for the resource */
|
|
38210
|
-
description:
|
|
39104
|
+
description: import_zod21.z.string().nullable().optional(),
|
|
38211
39105
|
/** Whether this is a distributed floating IP (requires floating-ip-distributed extension) */
|
|
38212
|
-
distributed:
|
|
39106
|
+
distributed: import_zod21.z.boolean().optional(),
|
|
38213
39107
|
/** A valid DNS domain (requires dns-integration extension) */
|
|
38214
|
-
dns_domain:
|
|
39108
|
+
dns_domain: import_zod21.z.string().optional(),
|
|
38215
39109
|
/** A valid DNS name (requires dns-integration extension) */
|
|
38216
|
-
dns_name:
|
|
39110
|
+
dns_name: import_zod21.z.string().optional(),
|
|
38217
39111
|
/** Time at which the resource was created (UTC ISO8601 format, requires standard-attr-timestamp extension) */
|
|
38218
39112
|
created_at: ISO8601TimestampSchema.optional(),
|
|
38219
39113
|
/** Time at which the resource was last updated (UTC ISO8601 format, requires standard-attr-timestamp extension) */
|
|
38220
39114
|
updated_at: ISO8601TimestampSchema.optional(),
|
|
38221
39115
|
/** The revision number of the resource (set by server, for concurrency control) */
|
|
38222
|
-
revision_number:
|
|
39116
|
+
revision_number: import_zod21.z.number(),
|
|
38223
39117
|
/** The ID of the project that owns the floating IP */
|
|
38224
|
-
project_id:
|
|
39118
|
+
project_id: import_zod21.z.string(),
|
|
38225
39119
|
/** The ID of the tenant (deprecated, use project_id) */
|
|
38226
|
-
tenant_id:
|
|
39120
|
+
tenant_id: import_zod21.z.string(),
|
|
38227
39121
|
/** The ID of the network associated with the floating IP */
|
|
38228
|
-
floating_network_id:
|
|
39122
|
+
floating_network_id: import_zod21.z.string(),
|
|
38229
39123
|
/** The fixed IP address associated with the floating IP (null if not associated) */
|
|
38230
|
-
fixed_ip_address:
|
|
39124
|
+
fixed_ip_address: import_zod21.z.string().nullable(),
|
|
38231
39125
|
/** The floating IP address */
|
|
38232
|
-
floating_ip_address:
|
|
39126
|
+
floating_ip_address: import_zod21.z.string(),
|
|
38233
39127
|
/** The ID of the port associated with the floating IP (null if not associated) */
|
|
38234
|
-
port_id:
|
|
39128
|
+
port_id: import_zod21.z.string().nullable(),
|
|
38235
39129
|
/** The ID of the floating IP address */
|
|
38236
|
-
id:
|
|
39130
|
+
id: import_zod21.z.string(),
|
|
38237
39131
|
/** The status of the floating IP */
|
|
38238
39132
|
status: FloatingIpStatusSchema,
|
|
38239
39133
|
/** The information of the associated port (null if not associated, requires fip-port-details extension) */
|
|
38240
39134
|
port_details: PortDetailsSchema.nullable().optional(),
|
|
38241
39135
|
/** The list of tags on the resource (requires standard-attr-tag extension) */
|
|
38242
|
-
tags:
|
|
39136
|
+
tags: import_zod21.z.array(import_zod21.z.string()).optional(),
|
|
38243
39137
|
/** The associated port forwarding resources for the floating IP (requires expose-port-forwarding-in-fip extension) */
|
|
38244
|
-
port_forwardings:
|
|
39138
|
+
port_forwardings: import_zod21.z.array(PortForwardingSchema).nullish(),
|
|
38245
39139
|
/** The ID of the QoS policy of the network where this floating IP is plugged (requires qos-fip extension) */
|
|
38246
|
-
qos_network_policy_id:
|
|
39140
|
+
qos_network_policy_id: import_zod21.z.string().optional(),
|
|
38247
39141
|
/** The ID of the QoS policy associated with the floating IP (requires qos extension) */
|
|
38248
|
-
qos_policy_id:
|
|
38249
|
-
});
|
|
38250
|
-
var FloatingIpIdInputSchema =
|
|
38251
|
-
project_id:
|
|
38252
|
-
floatingip_id:
|
|
38253
|
-
});
|
|
38254
|
-
var FloatingIpCreateRequestSchema =
|
|
38255
|
-
project_id:
|
|
38256
|
-
floating_network_id:
|
|
38257
|
-
fixed_ip_address:
|
|
38258
|
-
floating_ip_address:
|
|
38259
|
-
port_id:
|
|
38260
|
-
subnet_id:
|
|
38261
|
-
distributed:
|
|
38262
|
-
description:
|
|
38263
|
-
dns_domain:
|
|
38264
|
-
dns_name:
|
|
38265
|
-
qos_policy_id:
|
|
38266
|
-
});
|
|
38267
|
-
var FloatingIpUpdateRequestSchema =
|
|
38268
|
-
project_id:
|
|
38269
|
-
floatingip_id:
|
|
38270
|
-
port_id:
|
|
38271
|
-
fixed_ip_address:
|
|
38272
|
-
description:
|
|
38273
|
-
distributed:
|
|
38274
|
-
});
|
|
38275
|
-
var FloatingIpResponseSchema =
|
|
39142
|
+
qos_policy_id: import_zod21.z.string().optional()
|
|
39143
|
+
});
|
|
39144
|
+
var FloatingIpIdInputSchema = import_zod21.z.object({
|
|
39145
|
+
project_id: import_zod21.z.string(),
|
|
39146
|
+
floatingip_id: import_zod21.z.string()
|
|
39147
|
+
});
|
|
39148
|
+
var FloatingIpCreateRequestSchema = import_zod21.z.object({
|
|
39149
|
+
project_id: import_zod21.z.string().trim().min(1, "project_id must be a non-empty string"),
|
|
39150
|
+
floating_network_id: import_zod21.z.string(),
|
|
39151
|
+
fixed_ip_address: import_zod21.z.string().optional(),
|
|
39152
|
+
floating_ip_address: import_zod21.z.string().optional(),
|
|
39153
|
+
port_id: import_zod21.z.string().optional(),
|
|
39154
|
+
subnet_id: import_zod21.z.string().optional(),
|
|
39155
|
+
distributed: import_zod21.z.boolean().optional(),
|
|
39156
|
+
description: import_zod21.z.string().optional(),
|
|
39157
|
+
dns_domain: import_zod21.z.string().optional(),
|
|
39158
|
+
dns_name: import_zod21.z.string().optional(),
|
|
39159
|
+
qos_policy_id: import_zod21.z.string().optional()
|
|
39160
|
+
});
|
|
39161
|
+
var FloatingIpUpdateRequestSchema = import_zod21.z.object({
|
|
39162
|
+
project_id: import_zod21.z.string(),
|
|
39163
|
+
floatingip_id: import_zod21.z.string(),
|
|
39164
|
+
port_id: import_zod21.z.string().nullable(),
|
|
39165
|
+
fixed_ip_address: import_zod21.z.string().optional(),
|
|
39166
|
+
description: import_zod21.z.string().optional(),
|
|
39167
|
+
distributed: import_zod21.z.boolean().optional()
|
|
39168
|
+
});
|
|
39169
|
+
var FloatingIpResponseSchema = import_zod21.z.object({
|
|
38276
39170
|
floatingip: FloatingIpSchema
|
|
38277
39171
|
});
|
|
38278
|
-
var FloatingIpListResponseSchema =
|
|
39172
|
+
var FloatingIpListResponseSchema = import_zod21.z.object({
|
|
38279
39173
|
/** A list of floating IP objects */
|
|
38280
|
-
floatingips:
|
|
39174
|
+
floatingips: import_zod21.z.array(FloatingIpSchema)
|
|
38281
39175
|
});
|
|
38282
|
-
var FloatingIpQueryParametersSchema =
|
|
39176
|
+
var FloatingIpQueryParametersSchema = import_zod21.z.object({
|
|
38283
39177
|
/** Project ID for rescoping (required for projectScopedProcedure) */
|
|
38284
|
-
project_id:
|
|
39178
|
+
project_id: import_zod21.z.string(),
|
|
38285
39179
|
/** Filter by the ID of the floating IP */
|
|
38286
|
-
id:
|
|
39180
|
+
id: import_zod21.z.string().optional(),
|
|
38287
39181
|
/** Filter by the ID of the router for the floating IP */
|
|
38288
|
-
router_id:
|
|
39182
|
+
router_id: import_zod21.z.string().nullable().optional(),
|
|
38289
39183
|
/** Filter by the status of the floating IP */
|
|
38290
39184
|
status: FloatingIpStatusSchema.optional(),
|
|
38291
39185
|
/** Filter by the ID of the project that owns the resource */
|
|
38292
|
-
tenant_id:
|
|
39186
|
+
tenant_id: import_zod21.z.string().optional(),
|
|
38293
39187
|
/** Filter by the revision number of the resource */
|
|
38294
|
-
revision_number:
|
|
39188
|
+
revision_number: import_zod21.z.number().optional(),
|
|
38295
39189
|
/** Filter by the human-readable description of the resource */
|
|
38296
|
-
description:
|
|
39190
|
+
description: import_zod21.z.string().nullable().optional(),
|
|
38297
39191
|
/** Filter by the ID of the network associated with the floating IP */
|
|
38298
|
-
floating_network_id:
|
|
39192
|
+
floating_network_id: import_zod21.z.string().optional(),
|
|
38299
39193
|
/** Filter by the fixed IP address associated with the floating IP */
|
|
38300
|
-
fixed_ip_address:
|
|
39194
|
+
fixed_ip_address: import_zod21.z.string().optional(),
|
|
38301
39195
|
/** Filter by the floating IP address */
|
|
38302
|
-
floating_ip_address:
|
|
39196
|
+
floating_ip_address: import_zod21.z.string().optional(),
|
|
38303
39197
|
/** Filter by the ID of a port associated with the floating IP */
|
|
38304
|
-
port_id:
|
|
39198
|
+
port_id: import_zod21.z.string().nullable().optional(),
|
|
38305
39199
|
/** Sort direction (asc or desc) */
|
|
38306
39200
|
sort_dir: SortDirSchema.optional(),
|
|
38307
39201
|
/** Sort key - valid keys: fixed_ip_address, floating_ip_address, floating_network_id, id, router_id, status, tenant_id, project_id */
|
|
38308
|
-
sort_key:
|
|
39202
|
+
sort_key: import_zod21.z.enum([
|
|
38309
39203
|
"fixed_ip_address",
|
|
38310
39204
|
"floating_ip_address",
|
|
38311
39205
|
"floating_network_id",
|
|
@@ -38316,73 +39210,73 @@ var FloatingIpQueryParametersSchema = import_zod22.z.object({
|
|
|
38316
39210
|
"project_id"
|
|
38317
39211
|
]).optional(),
|
|
38318
39212
|
/** Filter by tags (comma-separated, resources must match all tags) - requires standard-attr-tag extension */
|
|
38319
|
-
tags:
|
|
39213
|
+
tags: import_zod21.z.array(import_zod21.z.string()).optional(),
|
|
38320
39214
|
/** Filter by tags with OR logic (comma-separated, resources match any tag) */
|
|
38321
|
-
"tags-any":
|
|
39215
|
+
"tags-any": import_zod21.z.string().optional(),
|
|
38322
39216
|
/** Exclude resources with these tags (comma-separated, resources must match all excluded tags) */
|
|
38323
|
-
"not-tags":
|
|
39217
|
+
"not-tags": import_zod21.z.string().optional(),
|
|
38324
39218
|
/** Exclude resources with these tags using OR logic (comma-separated, resources match any excluded tag) */
|
|
38325
|
-
"not-tags-any":
|
|
39219
|
+
"not-tags-any": import_zod21.z.string().optional(),
|
|
38326
39220
|
/** Specific fields to return (can be repeated) */
|
|
38327
|
-
fields:
|
|
38328
|
-
|
|
38329
|
-
|
|
39221
|
+
fields: import_zod21.z.union([
|
|
39222
|
+
import_zod21.z.string(),
|
|
39223
|
+
import_zod21.z.array(import_zod21.z.string())
|
|
38330
39224
|
]).optional(),
|
|
38331
39225
|
/** Pagination limit */
|
|
38332
|
-
limit:
|
|
39226
|
+
limit: import_zod21.z.number().optional(),
|
|
38333
39227
|
/** Pagination marker (ID of the last item in the previous list) */
|
|
38334
|
-
marker:
|
|
39228
|
+
marker: import_zod21.z.string().optional(),
|
|
38335
39229
|
/** Pagination direction (false = ascending, true = descending) */
|
|
38336
|
-
page_reverse:
|
|
39230
|
+
page_reverse: import_zod21.z.boolean().optional(),
|
|
38337
39231
|
// BFF-side search (filtered in BFF layer, not sent to OpenStack)
|
|
38338
|
-
searchTerm:
|
|
38339
|
-
});
|
|
38340
|
-
var ExternalNetworksQuerySchema =
|
|
38341
|
-
project_id:
|
|
38342
|
-
"router:external":
|
|
38343
|
-
});
|
|
38344
|
-
var ExternalNetworkSchema =
|
|
38345
|
-
id:
|
|
38346
|
-
name:
|
|
38347
|
-
project_id:
|
|
38348
|
-
"router:external":
|
|
38349
|
-
shared:
|
|
39232
|
+
searchTerm: import_zod21.z.string().optional()
|
|
39233
|
+
});
|
|
39234
|
+
var ExternalNetworksQuerySchema = import_zod21.z.object({
|
|
39235
|
+
project_id: import_zod21.z.string(),
|
|
39236
|
+
"router:external": import_zod21.z.literal(true).default(true)
|
|
39237
|
+
});
|
|
39238
|
+
var ExternalNetworkSchema = import_zod21.z.object({
|
|
39239
|
+
id: import_zod21.z.string(),
|
|
39240
|
+
name: import_zod21.z.string(),
|
|
39241
|
+
project_id: import_zod21.z.string(),
|
|
39242
|
+
"router:external": import_zod21.z.boolean().optional(),
|
|
39243
|
+
shared: import_zod21.z.boolean(),
|
|
38350
39244
|
status: NetworkPortStatusSchema,
|
|
38351
39245
|
updated_at: ISO8601TimestampSchema.optional(),
|
|
38352
|
-
is_default:
|
|
39246
|
+
is_default: import_zod21.z.boolean().optional()
|
|
38353
39247
|
});
|
|
38354
|
-
var ExternalNetworksResponseSchema =
|
|
38355
|
-
networks:
|
|
39248
|
+
var ExternalNetworksResponseSchema = import_zod21.z.object({
|
|
39249
|
+
networks: import_zod21.z.array(ExternalNetworkSchema)
|
|
38356
39250
|
});
|
|
38357
|
-
var DnsDomainSchema =
|
|
38358
|
-
id:
|
|
38359
|
-
name:
|
|
39251
|
+
var DnsDomainSchema = import_zod21.z.object({
|
|
39252
|
+
id: import_zod21.z.string(),
|
|
39253
|
+
name: import_zod21.z.string()
|
|
38360
39254
|
});
|
|
38361
|
-
var DnsDomainResponseSchema =
|
|
38362
|
-
zones:
|
|
39255
|
+
var DnsDomainResponseSchema = import_zod21.z.object({
|
|
39256
|
+
zones: import_zod21.z.array(DnsDomainSchema)
|
|
38363
39257
|
});
|
|
38364
|
-
var AvailablePortsQuerySchema =
|
|
38365
|
-
project_id:
|
|
39258
|
+
var AvailablePortsQuerySchema = import_zod21.z.object({
|
|
39259
|
+
project_id: import_zod21.z.string(),
|
|
38366
39260
|
/** Only ACTIVE ports are eligible for floating IP association */
|
|
38367
|
-
status:
|
|
39261
|
+
status: import_zod21.z.literal("ACTIVE").default("ACTIVE"),
|
|
38368
39262
|
/** Only administratively UP ports are eligible for floating IP association */
|
|
38369
|
-
admin_state_up:
|
|
39263
|
+
admin_state_up: import_zod21.z.literal(true).default(true)
|
|
38370
39264
|
});
|
|
38371
|
-
var AvailablePortSchema =
|
|
39265
|
+
var AvailablePortSchema = import_zod21.z.object({
|
|
38372
39266
|
/** The ID of the port */
|
|
38373
|
-
id:
|
|
39267
|
+
id: import_zod21.z.string(),
|
|
38374
39268
|
/** Human-readable name of the port */
|
|
38375
|
-
name:
|
|
39269
|
+
name: import_zod21.z.string().nullable().optional(),
|
|
38376
39270
|
/** List of fixed IPs assigned to the port, each entry pairs an IP address with its subnet. */
|
|
38377
|
-
fixed_ips:
|
|
39271
|
+
fixed_ips: import_zod21.z.array(import_zod21.z.object({
|
|
38378
39272
|
/** The fixed IP address assigned to the port */
|
|
38379
|
-
ip_address:
|
|
39273
|
+
ip_address: import_zod21.z.string(),
|
|
38380
39274
|
/** The ID of the subnet the IP belongs to */
|
|
38381
|
-
subnet_id:
|
|
39275
|
+
subnet_id: import_zod21.z.string().optional()
|
|
38382
39276
|
})).optional()
|
|
38383
39277
|
});
|
|
38384
|
-
var AvailablePortsResponseSchema =
|
|
38385
|
-
ports:
|
|
39278
|
+
var AvailablePortsResponseSchema = import_zod21.z.object({
|
|
39279
|
+
ports: import_zod21.z.array(AvailablePortSchema)
|
|
38386
39280
|
});
|
|
38387
39281
|
|
|
38388
39282
|
// src/server/Network/helpers/errorHandling.ts
|
|
@@ -38490,6 +39384,7 @@ var floatingIpRouter = {
|
|
|
38490
39384
|
const data = await response.json();
|
|
38491
39385
|
const { floatingips } = parseOrThrow(FloatingIpListResponseSchema, data, "floatingIpRouter.list");
|
|
38492
39386
|
return filterBySearchParams(floatingips, searchTerm, [
|
|
39387
|
+
"id",
|
|
38493
39388
|
"description",
|
|
38494
39389
|
"floating_ip_address",
|
|
38495
39390
|
"fixed_ip_address",
|
|
@@ -38643,110 +39538,112 @@ var floatingIpRouter = {
|
|
|
38643
39538
|
};
|
|
38644
39539
|
|
|
38645
39540
|
// src/server/Network/types/securityGroup.ts
|
|
38646
|
-
var
|
|
38647
|
-
var securityGroupRuleSchema =
|
|
38648
|
-
id:
|
|
38649
|
-
direction:
|
|
39541
|
+
var import_zod22 = require("zod");
|
|
39542
|
+
var securityGroupRuleSchema = import_zod22.z.object({
|
|
39543
|
+
id: import_zod22.z.string(),
|
|
39544
|
+
direction: import_zod22.z.enum([
|
|
38650
39545
|
"ingress",
|
|
38651
39546
|
"egress"
|
|
38652
39547
|
]).optional(),
|
|
38653
|
-
ethertype:
|
|
39548
|
+
ethertype: import_zod22.z.enum([
|
|
38654
39549
|
"IPv4",
|
|
38655
39550
|
"IPv6"
|
|
38656
39551
|
]).optional(),
|
|
38657
|
-
description:
|
|
38658
|
-
security_group_id:
|
|
38659
|
-
protocol:
|
|
38660
|
-
port_range_min:
|
|
38661
|
-
port_range_max:
|
|
38662
|
-
remote_ip_prefix:
|
|
38663
|
-
remote_group_id:
|
|
38664
|
-
remote_address_group_id:
|
|
38665
|
-
tenant_id:
|
|
38666
|
-
project_id:
|
|
38667
|
-
revision_number:
|
|
38668
|
-
tags:
|
|
38669
|
-
created_at:
|
|
38670
|
-
updated_at:
|
|
39552
|
+
description: import_zod22.z.string().nullable().optional(),
|
|
39553
|
+
security_group_id: import_zod22.z.string().optional(),
|
|
39554
|
+
protocol: import_zod22.z.string().nullable().optional(),
|
|
39555
|
+
port_range_min: import_zod22.z.number().nullable().optional(),
|
|
39556
|
+
port_range_max: import_zod22.z.number().nullable().optional(),
|
|
39557
|
+
remote_ip_prefix: import_zod22.z.string().nullable().optional(),
|
|
39558
|
+
remote_group_id: import_zod22.z.string().nullable().optional(),
|
|
39559
|
+
remote_address_group_id: import_zod22.z.string().nullable().optional(),
|
|
39560
|
+
tenant_id: import_zod22.z.string().nullable().optional(),
|
|
39561
|
+
project_id: import_zod22.z.string().nullable().optional(),
|
|
39562
|
+
revision_number: import_zod22.z.number().optional(),
|
|
39563
|
+
tags: import_zod22.z.array(import_zod22.z.string()).optional(),
|
|
39564
|
+
created_at: import_zod22.z.string().optional(),
|
|
39565
|
+
updated_at: import_zod22.z.string().nullable().optional()
|
|
38671
39566
|
});
|
|
38672
|
-
var securityGroupSchema =
|
|
38673
|
-
id:
|
|
38674
|
-
name:
|
|
38675
|
-
description:
|
|
38676
|
-
tenant_id:
|
|
38677
|
-
project_id:
|
|
38678
|
-
stateful:
|
|
38679
|
-
shared:
|
|
38680
|
-
tags:
|
|
38681
|
-
security_group_rules:
|
|
38682
|
-
revision_number:
|
|
38683
|
-
created_at:
|
|
38684
|
-
updated_at:
|
|
38685
|
-
});
|
|
38686
|
-
var securityGroupsResponseSchema =
|
|
38687
|
-
security_groups:
|
|
38688
|
-
});
|
|
38689
|
-
var securityGroupResponseSchema =
|
|
39567
|
+
var securityGroupSchema = import_zod22.z.object({
|
|
39568
|
+
id: import_zod22.z.string(),
|
|
39569
|
+
name: import_zod22.z.string().nullable().optional(),
|
|
39570
|
+
description: import_zod22.z.string().nullable().optional(),
|
|
39571
|
+
tenant_id: import_zod22.z.string().nullable().optional(),
|
|
39572
|
+
project_id: import_zod22.z.string().nullable().optional(),
|
|
39573
|
+
stateful: import_zod22.z.boolean().optional(),
|
|
39574
|
+
shared: import_zod22.z.boolean().optional(),
|
|
39575
|
+
tags: import_zod22.z.array(import_zod22.z.string()).optional(),
|
|
39576
|
+
security_group_rules: import_zod22.z.array(securityGroupRuleSchema).optional(),
|
|
39577
|
+
revision_number: import_zod22.z.number().optional(),
|
|
39578
|
+
created_at: import_zod22.z.string().optional(),
|
|
39579
|
+
updated_at: import_zod22.z.string().nullable().optional()
|
|
39580
|
+
});
|
|
39581
|
+
var securityGroupsResponseSchema = import_zod22.z.object({
|
|
39582
|
+
security_groups: import_zod22.z.array(securityGroupSchema)
|
|
39583
|
+
});
|
|
39584
|
+
var securityGroupResponseSchema = import_zod22.z.object({
|
|
38690
39585
|
security_group: securityGroupSchema
|
|
38691
39586
|
});
|
|
38692
|
-
var securityGroupRuleResponseSchema =
|
|
39587
|
+
var securityGroupRuleResponseSchema = import_zod22.z.object({
|
|
38693
39588
|
security_group_rule: securityGroupRuleSchema
|
|
38694
39589
|
});
|
|
38695
39590
|
var listSecurityGroupsInputSchema = projectScopedInputSchema.extend({
|
|
38696
39591
|
// Sorting
|
|
38697
|
-
sort_key:
|
|
39592
|
+
sort_key: import_zod22.z.string().optional(),
|
|
38698
39593
|
sort_dir: SortDirSchema.optional(),
|
|
38699
39594
|
// Basic filtering
|
|
38700
|
-
name:
|
|
38701
|
-
description:
|
|
38702
|
-
tenant_id:
|
|
38703
|
-
shared:
|
|
39595
|
+
name: import_zod22.z.string().optional(),
|
|
39596
|
+
description: import_zod22.z.string().optional(),
|
|
39597
|
+
tenant_id: import_zod22.z.string().optional(),
|
|
39598
|
+
shared: import_zod22.z.boolean().optional(),
|
|
38704
39599
|
// Tag-based filtering (string values follow Neutron semantics)
|
|
38705
|
-
tags:
|
|
38706
|
-
tags_any:
|
|
38707
|
-
not_tags:
|
|
38708
|
-
not_tags_any:
|
|
39600
|
+
tags: import_zod22.z.string().optional(),
|
|
39601
|
+
tags_any: import_zod22.z.string().optional(),
|
|
39602
|
+
not_tags: import_zod22.z.string().optional(),
|
|
39603
|
+
not_tags_any: import_zod22.z.string().optional(),
|
|
39604
|
+
// Filtered in the BFF layer, not forwarded to Neutron
|
|
39605
|
+
stateful: import_zod22.z.boolean().optional(),
|
|
38709
39606
|
// BFF-side search (filtered in BFF layer, not sent to OpenStack)
|
|
38710
|
-
searchTerm:
|
|
39607
|
+
searchTerm: import_zod22.z.string().optional()
|
|
38711
39608
|
});
|
|
38712
39609
|
var getSecurityGroupByIdInputSchema = projectScopedInputSchema.extend({
|
|
38713
|
-
securityGroupId:
|
|
39610
|
+
securityGroupId: import_zod22.z.string()
|
|
38714
39611
|
});
|
|
38715
39612
|
var createSecurityGroupInputSchema = projectScopedInputSchema.extend({
|
|
38716
|
-
name:
|
|
38717
|
-
description:
|
|
38718
|
-
stateful:
|
|
39613
|
+
name: import_zod22.z.string().min(1, "Name is required"),
|
|
39614
|
+
description: import_zod22.z.string().optional(),
|
|
39615
|
+
stateful: import_zod22.z.boolean().optional()
|
|
38719
39616
|
});
|
|
38720
39617
|
var deleteSecurityGroupInputSchema = projectScopedInputSchema.extend({
|
|
38721
|
-
securityGroupId:
|
|
39618
|
+
securityGroupId: import_zod22.z.string()
|
|
38722
39619
|
});
|
|
38723
39620
|
var updateSecurityGroupInputSchema = projectScopedInputSchema.extend({
|
|
38724
|
-
securityGroupId:
|
|
38725
|
-
name:
|
|
38726
|
-
description:
|
|
38727
|
-
stateful:
|
|
39621
|
+
securityGroupId: import_zod22.z.string(),
|
|
39622
|
+
name: import_zod22.z.string().min(1, "Name is required").optional(),
|
|
39623
|
+
description: import_zod22.z.string().optional(),
|
|
39624
|
+
stateful: import_zod22.z.boolean().optional()
|
|
38728
39625
|
});
|
|
38729
39626
|
var deleteSecurityGroupRuleInputSchema = projectScopedInputSchema.extend({
|
|
38730
|
-
ruleId:
|
|
39627
|
+
ruleId: import_zod22.z.string()
|
|
38731
39628
|
});
|
|
38732
39629
|
var createSecurityGroupRuleInputSchema = projectScopedInputSchema.extend({
|
|
38733
|
-
security_group_id:
|
|
38734
|
-
direction:
|
|
39630
|
+
security_group_id: import_zod22.z.string(),
|
|
39631
|
+
direction: import_zod22.z.enum([
|
|
38735
39632
|
"ingress",
|
|
38736
39633
|
"egress"
|
|
38737
39634
|
]),
|
|
38738
|
-
ethertype:
|
|
39635
|
+
ethertype: import_zod22.z.enum([
|
|
38739
39636
|
"IPv4",
|
|
38740
39637
|
"IPv6"
|
|
38741
39638
|
]).default("IPv4"),
|
|
38742
|
-
description:
|
|
38743
|
-
protocol:
|
|
38744
|
-
port_range_min:
|
|
38745
|
-
port_range_max:
|
|
39639
|
+
description: import_zod22.z.string().optional(),
|
|
39640
|
+
protocol: import_zod22.z.string().nullable().optional(),
|
|
39641
|
+
port_range_min: import_zod22.z.number().int().max(65535).nullable().optional(),
|
|
39642
|
+
port_range_max: import_zod22.z.number().int().max(65535).nullable().optional(),
|
|
38746
39643
|
// Transform empty strings to undefined for proper validation
|
|
38747
|
-
remote_ip_prefix:
|
|
38748
|
-
remote_group_id:
|
|
38749
|
-
remote_address_group_id:
|
|
39644
|
+
remote_ip_prefix: import_zod22.z.string().nullable().optional().transform((val) => val === "" ? void 0 : val),
|
|
39645
|
+
remote_group_id: import_zod22.z.string().nullable().optional().transform((val) => val === "" ? void 0 : val),
|
|
39646
|
+
remote_address_group_id: import_zod22.z.string().nullable().optional().transform((val) => val === "" ? void 0 : val)
|
|
38750
39647
|
});
|
|
38751
39648
|
|
|
38752
39649
|
// src/server/Network/helpers/securityGroupHelpers.ts
|
|
@@ -39048,6 +39945,10 @@ var deduplicateSecurityGroupsById = /* @__PURE__ */ __name((items) => {
|
|
|
39048
39945
|
}
|
|
39049
39946
|
return Array.from(seen.values());
|
|
39050
39947
|
}, "deduplicateSecurityGroupsById");
|
|
39948
|
+
var filterSecurityGroupsByStateful = /* @__PURE__ */ __name((items, stateful) => {
|
|
39949
|
+
if (stateful === void 0) return items;
|
|
39950
|
+
return items.filter((item) => (item.stateful ?? true) === stateful);
|
|
39951
|
+
}, "filterSecurityGroupsByStateful");
|
|
39051
39952
|
var sortSecurityGroups = /* @__PURE__ */ __name((items, sortKey, sortDir = "asc") => {
|
|
39052
39953
|
if (!sortKey) return items;
|
|
39053
39954
|
return [
|
|
@@ -39099,7 +40000,7 @@ __name(fetchSecurityGroupsWithParams, "fetchSecurityGroupsWithParams");
|
|
|
39099
40000
|
var securityGroupRouter = {
|
|
39100
40001
|
list: projectScopedProcedure.input(listSecurityGroupsInputSchema).query(async ({ input, ctx }) => {
|
|
39101
40002
|
return withErrorHandling3(async () => {
|
|
39102
|
-
const { searchTerm, project_id, shared, sort_key, sort_dir, ...queryInput } = input;
|
|
40003
|
+
const { searchTerm, project_id, shared, stateful, sort_key, sort_dir, ...queryInput } = input;
|
|
39103
40004
|
const network = getNetworkService(ctx);
|
|
39104
40005
|
if (shared !== void 0) {
|
|
39105
40006
|
const securityGroups = await fetchSecurityGroupsWithParams(network, {
|
|
@@ -39108,7 +40009,7 @@ var securityGroupRouter = {
|
|
|
39108
40009
|
sort_key,
|
|
39109
40010
|
sort_dir
|
|
39110
40011
|
});
|
|
39111
|
-
return filterBySearchParams(securityGroups, searchTerm, [
|
|
40012
|
+
return filterBySearchParams(filterSecurityGroupsByStateful(securityGroups, stateful), searchTerm, [
|
|
39112
40013
|
"name",
|
|
39113
40014
|
"description",
|
|
39114
40015
|
"id"
|
|
@@ -39129,6 +40030,7 @@ var securityGroupRouter = {
|
|
|
39129
40030
|
...ownGroups,
|
|
39130
40031
|
...sharedGroups
|
|
39131
40032
|
]);
|
|
40033
|
+
combined = filterSecurityGroupsByStateful(combined, stateful);
|
|
39132
40034
|
combined = filterBySearchParams(combined, searchTerm, [
|
|
39133
40035
|
"name",
|
|
39134
40036
|
"description",
|
|
@@ -39214,13 +40116,15 @@ var securityGroupRouter = {
|
|
|
39214
40116
|
};
|
|
39215
40117
|
|
|
39216
40118
|
// src/server/Network/routers/securityGroupRuleRouter.ts
|
|
40119
|
+
var import_signal_openstack9 = __toESM(require_esm());
|
|
39217
40120
|
var SECURITY_GROUP_RULES_BASE_URL = "v2.0/security-group-rules";
|
|
39218
40121
|
var securityGroupRuleRouter = {
|
|
39219
40122
|
delete: projectScopedProcedure.input(deleteSecurityGroupRuleInputSchema).mutation(async ({ input, ctx }) => {
|
|
39220
40123
|
return withErrorHandling3(async () => {
|
|
39221
40124
|
const { ruleId } = input;
|
|
39222
40125
|
const network = getNetworkService(ctx);
|
|
39223
|
-
const
|
|
40126
|
+
const encodedRuleId = (0, import_signal_openstack9.validateAndEncodeResourceId)(ruleId, "Security group rule");
|
|
40127
|
+
const response = await network.del(`${SECURITY_GROUP_RULES_BASE_URL}/${encodedRuleId}`);
|
|
39224
40128
|
if (!response?.ok) {
|
|
39225
40129
|
throw SecurityGroupRuleErrorHandlers.delete(response, ruleId);
|
|
39226
40130
|
}
|
|
@@ -39268,42 +40172,42 @@ var securityGroupRuleRouter = {
|
|
|
39268
40172
|
};
|
|
39269
40173
|
|
|
39270
40174
|
// src/server/Network/types/rbacPolicy.ts
|
|
39271
|
-
var
|
|
39272
|
-
var rbacPolicySchema =
|
|
39273
|
-
id:
|
|
39274
|
-
object_type:
|
|
40175
|
+
var import_zod23 = require("zod");
|
|
40176
|
+
var rbacPolicySchema = import_zod23.z.object({
|
|
40177
|
+
id: import_zod23.z.string(),
|
|
40178
|
+
object_type: import_zod23.z.enum([
|
|
39275
40179
|
"qos_policy",
|
|
39276
40180
|
"network",
|
|
39277
40181
|
"security_group"
|
|
39278
40182
|
]),
|
|
39279
|
-
object_id:
|
|
39280
|
-
action:
|
|
40183
|
+
object_id: import_zod23.z.string(),
|
|
40184
|
+
action: import_zod23.z.enum([
|
|
39281
40185
|
"access_as_shared",
|
|
39282
40186
|
"access_as_external"
|
|
39283
40187
|
]),
|
|
39284
|
-
target_tenant:
|
|
39285
|
-
tenant_id:
|
|
39286
|
-
project_id:
|
|
40188
|
+
target_tenant: import_zod23.z.string(),
|
|
40189
|
+
tenant_id: import_zod23.z.string().nullable().optional(),
|
|
40190
|
+
project_id: import_zod23.z.string().nullable().optional()
|
|
39287
40191
|
});
|
|
39288
|
-
var rbacPoliciesResponseSchema =
|
|
39289
|
-
rbac_policies:
|
|
40192
|
+
var rbacPoliciesResponseSchema = import_zod23.z.object({
|
|
40193
|
+
rbac_policies: import_zod23.z.array(rbacPolicySchema)
|
|
39290
40194
|
});
|
|
39291
|
-
var rbacPolicyResponseSchema =
|
|
40195
|
+
var rbacPolicyResponseSchema = import_zod23.z.object({
|
|
39292
40196
|
rbac_policy: rbacPolicySchema
|
|
39293
40197
|
});
|
|
39294
40198
|
var listRBACPoliciesForSecurityGroupInputSchema = projectScopedInputSchema.extend({
|
|
39295
|
-
securityGroupId:
|
|
40199
|
+
securityGroupId: import_zod23.z.string()
|
|
39296
40200
|
});
|
|
39297
40201
|
var createRBACPolicyInputSchema = projectScopedInputSchema.extend({
|
|
39298
|
-
securityGroupId:
|
|
39299
|
-
targetTenant:
|
|
40202
|
+
securityGroupId: import_zod23.z.string(),
|
|
40203
|
+
targetTenant: import_zod23.z.string().trim().min(1, "Target project ID is required")
|
|
39300
40204
|
});
|
|
39301
40205
|
var updateRBACPolicyInputSchema = projectScopedInputSchema.extend({
|
|
39302
|
-
policyId:
|
|
39303
|
-
targetTenant:
|
|
40206
|
+
policyId: import_zod23.z.string(),
|
|
40207
|
+
targetTenant: import_zod23.z.string().trim().min(1, "Target project ID is required")
|
|
39304
40208
|
});
|
|
39305
40209
|
var deleteRBACPolicyInputSchema = projectScopedInputSchema.extend({
|
|
39306
|
-
policyId:
|
|
40210
|
+
policyId: import_zod23.z.string()
|
|
39307
40211
|
});
|
|
39308
40212
|
|
|
39309
40213
|
// src/server/Network/helpers/rbacPolicyHelpers.ts
|
|
@@ -39461,6 +40365,7 @@ function parseRBACPoliciesListResponse(data, operation) {
|
|
|
39461
40365
|
__name(parseRBACPoliciesListResponse, "parseRBACPoliciesListResponse");
|
|
39462
40366
|
|
|
39463
40367
|
// src/server/Network/routers/rbacPolicyRouter.ts
|
|
40368
|
+
var import_signal_openstack10 = __toESM(require_esm());
|
|
39464
40369
|
var RBAC_POLICIES_BASE_URL = "v2.0/rbac-policies";
|
|
39465
40370
|
var rbacPolicyRouter = {
|
|
39466
40371
|
list: projectScopedProcedure.input(listRBACPoliciesForSecurityGroupInputSchema).query(async ({ input, ctx }) => {
|
|
@@ -39509,7 +40414,8 @@ var rbacPolicyRouter = {
|
|
|
39509
40414
|
target_tenant: targetTenant
|
|
39510
40415
|
}
|
|
39511
40416
|
};
|
|
39512
|
-
const
|
|
40417
|
+
const encodedPolicyId = (0, import_signal_openstack10.validateAndEncodeResourceId)(policyId, "RBAC policy");
|
|
40418
|
+
const response = await network.put(`${RBAC_POLICIES_BASE_URL}/${encodedPolicyId}`, requestBody);
|
|
39513
40419
|
if (!response.ok) {
|
|
39514
40420
|
throw RBACPolicyErrorHandlers.update(response, policyId);
|
|
39515
40421
|
}
|
|
@@ -39521,7 +40427,8 @@ var rbacPolicyRouter = {
|
|
|
39521
40427
|
return withErrorHandling3(async () => {
|
|
39522
40428
|
const { policyId } = input;
|
|
39523
40429
|
const network = getNetworkService(ctx);
|
|
39524
|
-
const
|
|
40430
|
+
const encodedPolicyId = (0, import_signal_openstack10.validateAndEncodeResourceId)(policyId, "RBAC policy");
|
|
40431
|
+
const response = await network.del(`${RBAC_POLICIES_BASE_URL}/${encodedPolicyId}`);
|
|
39525
40432
|
if (!response.ok) {
|
|
39526
40433
|
throw RBACPolicyErrorHandlers.delete(response, policyId);
|
|
39527
40434
|
}
|
|
@@ -39686,10 +40593,6 @@ var NETWORK_MAPPINGS = {
|
|
|
39686
40593
|
engine: "network",
|
|
39687
40594
|
rule: "create_security_group_rule"
|
|
39688
40595
|
},
|
|
39689
|
-
"network:security_group_rules:update": {
|
|
39690
|
-
engine: "network",
|
|
39691
|
-
rule: "update_security_group_rule"
|
|
39692
|
-
},
|
|
39693
40596
|
"network:security_group_rules:delete": {
|
|
39694
40597
|
engine: "network",
|
|
39695
40598
|
rule: "delete_security_group_rule"
|
|
@@ -39766,7 +40669,7 @@ var buildAppRouter = /* @__PURE__ */ __name((policyDir, extraRouters = []) => {
|
|
|
39766
40669
|
}, "buildAppRouter");
|
|
39767
40670
|
|
|
39768
40671
|
// src/server/context.ts
|
|
39769
|
-
var
|
|
40672
|
+
var import_signal_openstack11 = __toESM(require_esm());
|
|
39770
40673
|
|
|
39771
40674
|
// src/server/sessionCookie.ts
|
|
39772
40675
|
var import_cookie = require("@fastify/cookie");
|
|
@@ -39854,7 +40757,7 @@ async function createContext(opts, config) {
|
|
|
39854
40757
|
writable: false
|
|
39855
40758
|
});
|
|
39856
40759
|
if (currentAuthToken) {
|
|
39857
|
-
openstackSession = await (0,
|
|
40760
|
+
openstackSession = await (0, import_signal_openstack11.SignalOpenstackSession)(normalizedEndpoint, {
|
|
39858
40761
|
auth: {
|
|
39859
40762
|
identity: {
|
|
39860
40763
|
methods: [
|
|
@@ -39917,7 +40820,7 @@ async function createContext(opts, config) {
|
|
|
39917
40820
|
}
|
|
39918
40821
|
}, "getUserInfo");
|
|
39919
40822
|
const createSession = /* @__PURE__ */ __name(async (params) => {
|
|
39920
|
-
openstackSession = await (0,
|
|
40823
|
+
openstackSession = await (0, import_signal_openstack11.SignalOpenstackSession)(normalizedEndpoint, {
|
|
39921
40824
|
auth: {
|
|
39922
40825
|
identity: {
|
|
39923
40826
|
methods: [
|
|
@@ -40061,7 +40964,7 @@ __name(createContext, "createContext");
|
|
|
40061
40964
|
// src/server/server.ts
|
|
40062
40965
|
var import_path2 = __toESM(require("path"));
|
|
40063
40966
|
var import_node_stream4 = require("stream");
|
|
40064
|
-
var
|
|
40967
|
+
var import_zod24 = require("zod");
|
|
40065
40968
|
|
|
40066
40969
|
// src/server/aurora-fastify-plugins/csrfProtection.ts
|
|
40067
40970
|
var import_csrf_protection = __toESM(require("@fastify/csrf-protection"));
|
|
@@ -40262,7 +41165,7 @@ async function createServer(config) {
|
|
|
40262
41165
|
router: appRouter,
|
|
40263
41166
|
createContext: /* @__PURE__ */ __name((opts) => createContext(opts, contextConfig), "createContext"),
|
|
40264
41167
|
onError: /* @__PURE__ */ __name((err) => {
|
|
40265
|
-
if (err.error.cause instanceof
|
|
41168
|
+
if (err.error.cause instanceof import_zod24.ZodError) err.error.message = err.error.cause.issues.map((e) => e.message).join(",");
|
|
40266
41169
|
}, "onError")
|
|
40267
41170
|
}
|
|
40268
41171
|
});
|