@cobaltcore-dev/aurora 2.0.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-C2MqUgGR.mjs → DeleteFlavorModal-Be2GFiB_.mjs} +3 -2
- package/dist/client/DeleteFlavorModal-Be2GFiB_.mjs.map +1 -0
- package/dist/client/{RouteIdLevelDefaultError-CSseJ1yG.mjs → RouteIdLevelDefaultError-DPF_x2tG.mjs} +9 -9
- 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-DYv_StX22.mjs → _flavorId-0PioWpEp.mjs} +4 -4
- package/dist/client/_flavorId-0PioWpEp.mjs.map +1 -0
- package/dist/client/{_flavorId-YV3jCS5g.mjs → _flavorId-B7fp6Nk8.mjs} +3 -3
- package/dist/client/{_flavorId-YV3jCS5g.mjs.map → _flavorId-B7fp6Nk8.mjs.map} +1 -1
- package/dist/client/{_flavorId-CaA4-Pnf.mjs → _flavorId-CgueqKTs.mjs} +3 -3
- package/dist/client/{_flavorId-CaA4-Pnf.mjs.map → _flavorId-CgueqKTs.mjs.map} +1 -1
- package/dist/client/{_floatingIpId-Dpsc7GU4.mjs → _floatingIpId-B8r64AL9.mjs} +3 -3
- package/dist/client/{_floatingIpId-Dpsc7GU4.mjs.map → _floatingIpId-B8r64AL9.mjs.map} +1 -1
- package/dist/client/{_floatingIpId-BeYElanB.mjs → _floatingIpId-lEobpOER.mjs} +2 -2
- package/dist/client/{_floatingIpId-BeYElanB.mjs.map → _floatingIpId-lEobpOER.mjs.map} +1 -1
- package/dist/client/{_imageId-pBSS0YcC.mjs → _imageId-Ccv8PedW.mjs} +3 -3
- package/dist/client/{_imageId-pBSS0YcC.mjs.map → _imageId-Ccv8PedW.mjs.map} +1 -1
- package/dist/client/_imageId-CiqEQB_J.mjs +558 -0
- package/dist/client/{_imageId-mfKp3JHn.mjs.map → _imageId-CiqEQB_J.mjs.map} +1 -1
- package/dist/client/{_imageId-mpXUMCh_.mjs → _imageId-D9LR-02T.mjs} +3 -3
- package/dist/client/{_imageId-mpXUMCh_.mjs.map → _imageId-D9LR-02T.mjs.map} +1 -1
- package/dist/client/{_projectId-BnTBxKfp.mjs → _projectId-BDARsteX.mjs} +3 -3
- package/dist/client/{_projectId-BnTBxKfp.mjs.map → _projectId-BDARsteX.mjs.map} +1 -1
- package/dist/client/{_projectId-CVrdJ52s.mjs → _projectId-CtyAHqhH.mjs} +2 -2
- package/dist/client/{_projectId-CVrdJ52s.mjs.map → _projectId-CtyAHqhH.mjs.map} +1 -1
- package/dist/client/_projectId-D3VOH5lN.mjs +94 -0
- package/dist/client/_projectId-D3VOH5lN.mjs.map +1 -0
- package/dist/client/{_projectId-AjgJX6Gl.mjs → _projectId-ZBk6vka0.mjs} +83 -81
- package/dist/client/_projectId-ZBk6vka0.mjs.map +1 -0
- package/dist/client/{_securityGroupId-AO3wnH8g.mjs → _securityGroupId-BwgZnoiR.mjs} +2 -2
- package/dist/client/{_securityGroupId-AO3wnH8g.mjs.map → _securityGroupId-BwgZnoiR.mjs.map} +1 -1
- package/dist/client/{_securityGroupId-Bq0ZyG6z.mjs → _securityGroupId-Cz2NUHdB.mjs} +3 -3
- package/dist/client/{_securityGroupId-Bq0ZyG6z.mjs.map → _securityGroupId-Cz2NUHdB.mjs.map} +1 -1
- 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-c-DAu8qr.mjs → _storageType-UqAMP-8d.mjs} +286 -285
- 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/Errors/RouteIdLevelDefaultError.d.ts +2 -1
- package/dist/client/{flavors-ZudiAiTU.mjs → flavors-Ce3J-cFa.mjs} +2 -2
- package/dist/client/{flavors-ZudiAiTU.mjs.map → flavors-Ce3J-cFa.mjs.map} +1 -1
- package/dist/client/{flavors-Bgq3KZmx.mjs → flavors-D5ThLaXO.mjs} +84 -77
- package/dist/client/flavors-D5ThLaXO.mjs.map +1 -0
- package/dist/client/{floatingips-B1oLH8z6.mjs → floatingips-DXnRlLvV.mjs} +2 -2
- package/dist/client/{floatingips-B1oLH8z6.mjs.map → floatingips-DXnRlLvV.mjs.map} +1 -1
- package/dist/client/{floatingips-BtLg3yEQ.mjs → floatingips-HRbuLxiN.mjs} +3 -3
- package/dist/client/{floatingips-BtLg3yEQ.mjs.map → floatingips-HRbuLxiN.mjs.map} +1 -1
- package/dist/client/{floatingips-BeQEkVH8.mjs → floatingips-nN1kQmsu.mjs} +2 -2
- package/dist/client/{floatingips-BeQEkVH8.mjs.map → floatingips-nN1kQmsu.mjs.map} +1 -1
- package/dist/client/{images-CnTW3bpy.mjs → images-C8M3ysWR.mjs} +3 -3
- package/dist/client/{images-CnTW3bpy.mjs.map → images-C8M3ysWR.mjs.map} +1 -1
- package/dist/client/{images-DZHDYDQu.mjs → images-Cf5R28NR.mjs} +2 -2
- package/dist/client/{images-DZHDYDQu.mjs.map → images-Cf5R28NR.mjs.map} +1 -1
- package/dist/client/index.js +198 -197
- package/dist/client/index.js.map +1 -1
- package/dist/client/{objects-jYQBFzwg.mjs → objects-BudUAfWY.mjs} +4265 -4275
- 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-BRndk7wh.mjs → securitygroups-67fFxiwt.mjs} +2 -2
- package/dist/client/{securitygroups-BRndk7wh.mjs.map → securitygroups-67fFxiwt.mjs.map} +1 -1
- package/dist/client/{securitygroups--0PS2wbM.mjs → securitygroups-DXrfyuVF.mjs} +3 -3
- package/dist/client/{securitygroups--0PS2wbM.mjs.map → securitygroups-DXrfyuVF.mjs.map} +1 -1
- package/dist/client/{securitygroups-Cs2Op-tu.mjs → securitygroups-WLB1_IM6.mjs} +3 -3
- package/dist/client/{securitygroups-Cs2Op-tu.mjs.map → securitygroups-WLB1_IM6.mjs.map} +1 -1
- package/dist/client/{securitygroups-CdN4yAAw.mjs → securitygroups-o0IXZM6S.mjs} +2 -2
- package/dist/client/{securitygroups-CdN4yAAw.mjs.map → securitygroups-o0IXZM6S.mjs.map} +1 -1
- 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 +96 -3
- package/dist/client/utils/serviceCatalog.d.ts +9 -0
- package/dist/client/utils/storageProviders.d.ts +44 -0
- package/dist/server/index.d.ts +96 -3
- package/dist/server/index.js +1224 -387
- package/package.json +1 -1
- package/dist/client/ContentHeader-CPJbgYWn.mjs +0 -115
- package/dist/client/ContentHeader-CPJbgYWn.mjs.map +0 -1
- package/dist/client/DeleteFlavorModal-C2MqUgGR.mjs.map +0 -1
- package/dist/client/RouteIdLevelDefaultError-CSseJ1yG.mjs.map +0 -1
- package/dist/client/_flavorId-DYv_StX22.mjs.map +0 -1
- package/dist/client/_imageId-mfKp3JHn.mjs +0 -558
- package/dist/client/_projectId-AjgJX6Gl.mjs.map +0 -1
- package/dist/client/_projectId-DgciZslU.mjs +0 -92
- package/dist/client/_projectId-DgciZslU.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-CzZ4u9x_.mjs.map +0 -1
- package/dist/client/_storageType-c-DAu8qr.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-Bgq3KZmx.mjs.map +0 -1
- package/dist/client/objects-25xsh4Ia.mjs +0 -66
- package/dist/client/objects-25xsh4Ia.mjs.map +0 -1
- package/dist/client/objects-KXY6dgwq.mjs +0 -82
- package/dist/client/objects-KXY6dgwq.mjs.map +0 -1
- package/dist/client/objects-QryuClU3.mjs +0 -82
- package/dist/client/objects-QryuClU3.mjs.map +0 -1
- package/dist/client/objects-jYQBFzwg.mjs.map +0 -1
package/dist/server/index.js
CHANGED
|
@@ -28658,6 +28658,22 @@ var openstackErrorMiddleware = t.middleware(async ({ next }) => {
|
|
|
28658
28658
|
}
|
|
28659
28659
|
return result;
|
|
28660
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");
|
|
28661
28677
|
var router = t.router;
|
|
28662
28678
|
var auroraRouter = t.router;
|
|
28663
28679
|
var mergeRouters = t.mergeRouters;
|
|
@@ -28683,13 +28699,26 @@ var domainScopedInputSchema = import_zod.z.object({
|
|
|
28683
28699
|
var projectScopedProcedure = protectedProcedure.input(projectScopedInputSchema).use(/* @__PURE__ */ __name(async function rescopeToProject(opts) {
|
|
28684
28700
|
const { ctx, next, input } = opts;
|
|
28685
28701
|
const { project_id } = input;
|
|
28686
|
-
|
|
28687
|
-
|
|
28688
|
-
|
|
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
|
+
}
|
|
28689
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
|
+
}
|
|
28690
28719
|
throw new import_server.TRPCError({
|
|
28691
|
-
code: "
|
|
28692
|
-
message: "
|
|
28720
|
+
code: "NOT_FOUND",
|
|
28721
|
+
message: "Project not found or not accessible with your current session."
|
|
28693
28722
|
});
|
|
28694
28723
|
}
|
|
28695
28724
|
return next({
|
|
@@ -28717,13 +28746,26 @@ var domainScopedProcedure = protectedProcedure.input(domainScopedInputSchema).us
|
|
|
28717
28746
|
message: `Access denied. User does not have access to domain ${domain_id}`
|
|
28718
28747
|
});
|
|
28719
28748
|
}
|
|
28720
|
-
|
|
28721
|
-
|
|
28722
|
-
|
|
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
|
+
}
|
|
28723
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
|
+
}
|
|
28724
28766
|
throw new import_server.TRPCError({
|
|
28725
|
-
code: "
|
|
28726
|
-
message: "
|
|
28767
|
+
code: "NOT_FOUND",
|
|
28768
|
+
message: "Domain not found or not accessible with your current session."
|
|
28727
28769
|
});
|
|
28728
28770
|
}
|
|
28729
28771
|
return next({
|
|
@@ -28785,9 +28827,16 @@ var sessionRouter = {
|
|
|
28785
28827
|
setCurrentScope: protectedProcedure.input(discriminatedSchema).mutation(async ({ input, ctx }) => {
|
|
28786
28828
|
switch (input.type) {
|
|
28787
28829
|
case "domain": {
|
|
28788
|
-
|
|
28789
|
-
|
|
28790
|
-
|
|
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
|
+
}
|
|
28791
28840
|
const token = session?.getToken();
|
|
28792
28841
|
if (!token?.tokenData.domain) {
|
|
28793
28842
|
throw new import_server2.TRPCError({
|
|
@@ -28801,9 +28850,16 @@ var sessionRouter = {
|
|
|
28801
28850
|
};
|
|
28802
28851
|
}
|
|
28803
28852
|
case "project": {
|
|
28804
|
-
|
|
28805
|
-
|
|
28806
|
-
|
|
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
|
+
}
|
|
28807
28863
|
const token = session?.getToken();
|
|
28808
28864
|
if (!token?.tokenData.project) {
|
|
28809
28865
|
throw new import_server2.TRPCError({
|
|
@@ -29037,7 +29093,13 @@ function filterBySearchParams(items, searchTerm, searchFields) {
|
|
|
29037
29093
|
const searchLower = searchTerm.toLowerCase().trim();
|
|
29038
29094
|
return items.filter((item) => searchFields.some((field) => {
|
|
29039
29095
|
const value = item[field];
|
|
29040
|
-
|
|
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;
|
|
29041
29103
|
}));
|
|
29042
29104
|
}
|
|
29043
29105
|
__name(filterBySearchParams, "filterBySearchParams");
|
|
@@ -29990,7 +30052,10 @@ var imageRouter = {
|
|
|
29990
30052
|
let filteredImages = allImages;
|
|
29991
30053
|
if (hasSearchTerm) {
|
|
29992
30054
|
filteredImages = filterBySearchParams(filteredImages, queryInput.name, [
|
|
29993
|
-
"
|
|
30055
|
+
"id",
|
|
30056
|
+
"name",
|
|
30057
|
+
"owner",
|
|
30058
|
+
"size"
|
|
29994
30059
|
]);
|
|
29995
30060
|
}
|
|
29996
30061
|
if (queryInput.visibility && queryInput.visibility !== "all") {
|
|
@@ -30070,7 +30135,10 @@ var imageRouter = {
|
|
|
30070
30135
|
let filteredImages = allImages;
|
|
30071
30136
|
if (queryInput.name && queryInput.name.trim()) {
|
|
30072
30137
|
filteredImages = filterBySearchParams(filteredImages, queryInput.name, [
|
|
30073
|
-
"
|
|
30138
|
+
"id",
|
|
30139
|
+
"name",
|
|
30140
|
+
"owner",
|
|
30141
|
+
"size"
|
|
30074
30142
|
]);
|
|
30075
30143
|
}
|
|
30076
30144
|
if (queryInput.visibility && queryInput.visibility !== "all") {
|
|
@@ -30609,6 +30677,7 @@ var imageRouter = {
|
|
|
30609
30677
|
});
|
|
30610
30678
|
if (name) {
|
|
30611
30679
|
filteredImages = filterBySearchParams(filteredImages, name, [
|
|
30680
|
+
"id",
|
|
30612
30681
|
"name"
|
|
30613
30682
|
]);
|
|
30614
30683
|
}
|
|
@@ -31172,9 +31241,18 @@ function includesSearchTerm(flavor, searchTerm) {
|
|
|
31172
31241
|
const searchableValues = [
|
|
31173
31242
|
flavor.id,
|
|
31174
31243
|
flavor.name,
|
|
31175
|
-
flavor.description
|
|
31244
|
+
flavor.description,
|
|
31245
|
+
flavor.vcpus,
|
|
31246
|
+
flavor.ram,
|
|
31247
|
+
flavor.disk,
|
|
31248
|
+
flavor.swap
|
|
31176
31249
|
];
|
|
31177
|
-
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
|
+
});
|
|
31178
31256
|
}
|
|
31179
31257
|
__name(includesSearchTerm, "includesSearchTerm");
|
|
31180
31258
|
function filterAndSortFlavors(flavors, searchTerm, sortBy, sortDirection) {
|
|
@@ -31195,6 +31273,26 @@ function filterAndSortFlavors(flavors, searchTerm, sortBy, sortDirection) {
|
|
|
31195
31273
|
return result;
|
|
31196
31274
|
}
|
|
31197
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");
|
|
31198
31296
|
async function getFlavorById(compute, flavorId) {
|
|
31199
31297
|
if (!flavorId || flavorId.trim() === "") {
|
|
31200
31298
|
throw new import_server7.TRPCError({
|
|
@@ -31217,7 +31315,13 @@ async function getFlavorById(compute, flavorId) {
|
|
|
31217
31315
|
}
|
|
31218
31316
|
let response;
|
|
31219
31317
|
try {
|
|
31220
|
-
|
|
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);
|
|
31221
31325
|
} catch (error) {
|
|
31222
31326
|
if (error instanceof import_server7.TRPCError) throw error;
|
|
31223
31327
|
const statusCode = getStatusCodeFromError(error);
|
|
@@ -31249,11 +31353,20 @@ __name(getFlavorById, "getFlavorById");
|
|
|
31249
31353
|
async function fetchFlavors(compute, isPublic) {
|
|
31250
31354
|
let response;
|
|
31251
31355
|
try {
|
|
31252
|
-
|
|
31356
|
+
const maxVersion = await getMaxMicroversion(compute);
|
|
31357
|
+
const options = supportsDescriptionField(maxVersion) ? {
|
|
31358
|
+
queryParams: {
|
|
31359
|
+
is_public: isPublic
|
|
31360
|
+
},
|
|
31361
|
+
headers: {
|
|
31362
|
+
"OpenStack-API-Version": `compute ${maxVersion}`
|
|
31363
|
+
}
|
|
31364
|
+
} : {
|
|
31253
31365
|
queryParams: {
|
|
31254
31366
|
is_public: isPublic
|
|
31255
31367
|
}
|
|
31256
|
-
}
|
|
31368
|
+
};
|
|
31369
|
+
response = await compute.get("flavors/detail", options);
|
|
31257
31370
|
} catch (error) {
|
|
31258
31371
|
if (error instanceof import_server7.TRPCError) throw error;
|
|
31259
31372
|
const statusCode = getStatusCodeFromError(error);
|
|
@@ -31282,16 +31395,32 @@ async function fetchFlavors(compute, isPublic) {
|
|
|
31282
31395
|
}
|
|
31283
31396
|
}
|
|
31284
31397
|
__name(fetchFlavors, "fetchFlavors");
|
|
31285
|
-
async function createFlavor(compute, flavorData) {
|
|
31398
|
+
async function createFlavor(compute, flavorData, microversion) {
|
|
31286
31399
|
const requestBody = {
|
|
31287
31400
|
flavor: flavorData
|
|
31288
31401
|
};
|
|
31402
|
+
const headers = {};
|
|
31403
|
+
if (microversion && flavorData.description && supportsDescriptionField(microversion)) {
|
|
31404
|
+
headers["OpenStack-API-Version"] = `compute ${microversion}`;
|
|
31405
|
+
}
|
|
31289
31406
|
let response;
|
|
31290
31407
|
try {
|
|
31291
|
-
response = await compute.post("flavors", requestBody)
|
|
31408
|
+
response = await compute.post("flavors", requestBody, Object.keys(headers).length ? {
|
|
31409
|
+
headers
|
|
31410
|
+
} : void 0);
|
|
31292
31411
|
} catch (error) {
|
|
31293
31412
|
if (error instanceof import_server7.TRPCError) throw error;
|
|
31294
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
|
+
}
|
|
31295
31424
|
handleHttpError(statusCode, CREATE_FLAVOR_STATUS_MAP, ERROR_CODES.CREATE_FLAVOR_FAILED);
|
|
31296
31425
|
}
|
|
31297
31426
|
if (!response.ok) {
|
|
@@ -31302,7 +31431,6 @@ async function createFlavor(compute, flavorData) {
|
|
|
31302
31431
|
const jsonData = JSON.parse(rawData);
|
|
31303
31432
|
return jsonData.flavor;
|
|
31304
31433
|
} catch (error) {
|
|
31305
|
-
console.error("Error parsing flavor response:", error);
|
|
31306
31434
|
throw new import_server7.TRPCError({
|
|
31307
31435
|
code: "INTERNAL_SERVER_ERROR",
|
|
31308
31436
|
message: ERROR_CODES.CREATE_FLAVOR_FAILED,
|
|
@@ -31583,6 +31711,38 @@ __name(removeTenantAccess, "removeTenantAccess");
|
|
|
31583
31711
|
var import_server8 = require("@trpc/server");
|
|
31584
31712
|
var import_signal_openstack3 = __toESM(require_esm());
|
|
31585
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
|
+
}),
|
|
31586
31746
|
getFlavorById: projectScopedProcedure.input(import_zod9.z.object({
|
|
31587
31747
|
project_id: import_zod9.z.string(),
|
|
31588
31748
|
flavorId: import_zod9.z.string()
|
|
@@ -31670,7 +31830,8 @@ var flavorRouter = {
|
|
|
31670
31830
|
swap: import_zod9.z.number().optional(),
|
|
31671
31831
|
rxtx_factor: import_zod9.z.number().optional(),
|
|
31672
31832
|
"OS-FLV-EXT-DATA:ephemeral": import_zod9.z.number().optional(),
|
|
31673
|
-
"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()
|
|
31674
31835
|
})
|
|
31675
31836
|
})).mutation(async ({ input, ctx }) => {
|
|
31676
31837
|
try {
|
|
@@ -31682,11 +31843,35 @@ var flavorRouter = {
|
|
|
31682
31843
|
message: ERROR_CODES.COMPUTE_SERVICE_UNAVAILABLE
|
|
31683
31844
|
});
|
|
31684
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
|
+
}
|
|
31685
31870
|
const flavorData = {
|
|
31686
|
-
...
|
|
31871
|
+
...flavorToSend,
|
|
31687
31872
|
"OS-FLV-EXT-DATA:ephemeral": flavor["OS-FLV-EXT-DATA:ephemeral"] || 0
|
|
31688
31873
|
};
|
|
31689
|
-
const result = await createFlavor(compute, flavorData);
|
|
31874
|
+
const result = await createFlavor(compute, flavorData, supportsDescription ? detectedVersion : void 0);
|
|
31690
31875
|
return result;
|
|
31691
31876
|
} catch (error) {
|
|
31692
31877
|
if (error instanceof import_server8.TRPCError) {
|
|
@@ -34419,6 +34604,16 @@ __name(resolveEC2Credential, "resolveEC2Credential");
|
|
|
34419
34604
|
|
|
34420
34605
|
// src/server/Storage/clients/s3Client.ts
|
|
34421
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
|
|
34422
34617
|
function createS3Client(access, secret, endpoint, region = "default") {
|
|
34423
34618
|
if (!access?.trim()) {
|
|
34424
34619
|
throw new Error("S3 access key is required");
|
|
@@ -34436,7 +34631,10 @@ function createS3Client(access, secret, endpoint, region = "default") {
|
|
|
34436
34631
|
accessKeyId: access,
|
|
34437
34632
|
secretAccessKey: secret
|
|
34438
34633
|
},
|
|
34439
|
-
forcePathStyle: true
|
|
34634
|
+
forcePathStyle: true,
|
|
34635
|
+
requestHandler: {
|
|
34636
|
+
connectionTimeout: S3_CONNECTION_TIMEOUT_MS
|
|
34637
|
+
}
|
|
34440
34638
|
});
|
|
34441
34639
|
}
|
|
34442
34640
|
__name(createS3Client, "createS3Client");
|
|
@@ -34547,6 +34745,8 @@ var cephUploadProcedure = protectedProcedure.use(/* @__PURE__ */ __name(async fu
|
|
|
34547
34745
|
var import_server13 = require("@trpc/server");
|
|
34548
34746
|
var import_signal_openstack4 = __toESM(require_esm());
|
|
34549
34747
|
var S3_ERROR_MAP = {
|
|
34748
|
+
NotFound: "NOT_FOUND",
|
|
34749
|
+
Forbidden: "FORBIDDEN",
|
|
34550
34750
|
NoSuchBucket: "NOT_FOUND",
|
|
34551
34751
|
NoSuchKey: "NOT_FOUND",
|
|
34552
34752
|
NoSuchUpload: "NOT_FOUND",
|
|
@@ -34619,12 +34819,6 @@ __name(mapS3ErrorToTRPCError, "mapS3ErrorToTRPCError");
|
|
|
34619
34819
|
|
|
34620
34820
|
// src/server/Storage/types/ceph.ts
|
|
34621
34821
|
var import_zod13 = require("zod");
|
|
34622
|
-
|
|
34623
|
-
// src/server/Storage/constants.ts
|
|
34624
|
-
var S3_MAX_KEYS_PER_REQUEST = 1e3;
|
|
34625
|
-
var S3_PRESIGN_MAX_EXPIRY_SECONDS = 604800;
|
|
34626
|
-
|
|
34627
|
-
// src/server/Storage/types/ceph.ts
|
|
34628
34822
|
var ec2CredentialSchema = import_zod13.z.object({
|
|
34629
34823
|
id: import_zod13.z.string(),
|
|
34630
34824
|
access: import_zod13.z.string(),
|
|
@@ -34660,6 +34854,59 @@ var createBucketOutputSchema = import_zod13.z.object({
|
|
|
34660
34854
|
var deleteBucketInputSchema = projectScopedInputSchema.extend({
|
|
34661
34855
|
bucketName: import_zod13.z.string().min(1)
|
|
34662
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
|
+
});
|
|
34663
34910
|
var s3StatusSchema = import_zod13.z.object({
|
|
34664
34911
|
hasCredentials: import_zod13.z.boolean()
|
|
34665
34912
|
});
|
|
@@ -34727,6 +34974,9 @@ var deleteVersionsBulkInputSchema = projectScopedInputSchema.extend({
|
|
|
34727
34974
|
versionId: import_zod13.z.string().min(1)
|
|
34728
34975
|
})).min(1).max(1e4)
|
|
34729
34976
|
});
|
|
34977
|
+
var deleteNonCurrentVersionsInputSchema = projectScopedInputSchema.extend({
|
|
34978
|
+
containerName: existingBucketNameSchema
|
|
34979
|
+
});
|
|
34730
34980
|
var deletedObjectSchema = import_zod13.z.object({
|
|
34731
34981
|
key: import_zod13.z.string(),
|
|
34732
34982
|
versionId: import_zod13.z.string().optional(),
|
|
@@ -34745,6 +34995,22 @@ var deleteObjectsBulkOutputSchema = import_zod13.z.object({
|
|
|
34745
34995
|
deletedCount: import_zod13.z.number().int().nonnegative(),
|
|
34746
34996
|
errorCount: import_zod13.z.number().int().nonnegative()
|
|
34747
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
|
+
});
|
|
34748
35014
|
var createFolderInputSchema2 = projectScopedInputSchema.extend({
|
|
34749
35015
|
containerName: import_zod13.z.string().min(1),
|
|
34750
35016
|
folderPath: import_zod13.z.string().min(1)
|
|
@@ -34863,19 +35129,6 @@ var bucketPolicyStatementSchema = import_zod13.z.object({
|
|
|
34863
35129
|
message: "Principal object must contain at least one of: AWS, Service, or Federated"
|
|
34864
35130
|
});
|
|
34865
35131
|
}
|
|
34866
|
-
if (typeof val === "object" && val.AWS) {
|
|
34867
|
-
const arns = Array.isArray(val.AWS) ? val.AWS : [
|
|
34868
|
-
val.AWS
|
|
34869
|
-
];
|
|
34870
|
-
for (const arn of arns) {
|
|
34871
|
-
if (arn !== "*" && !/^arn:aws:iam::\d{12}:(?:root|user\/.+|role\/.+)$/.test(arn)) {
|
|
34872
|
-
ctx.addIssue({
|
|
34873
|
-
code: "custom",
|
|
34874
|
-
message: `Invalid AWS principal ARN format: ${arn}. Expected arn:aws:iam::ACCOUNT-ID:root or arn:aws:iam::ACCOUNT-ID:(user|role)/NAME`
|
|
34875
|
-
});
|
|
34876
|
-
}
|
|
34877
|
-
}
|
|
34878
|
-
}
|
|
34879
35132
|
}),
|
|
34880
35133
|
NotPrincipal: import_zod13.z.union([
|
|
34881
35134
|
import_zod13.z.string(),
|
|
@@ -35272,6 +35525,193 @@ var containerRouter = {
|
|
|
35272
35525
|
}
|
|
35273
35526
|
}),
|
|
35274
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
|
+
/**
|
|
35275
35715
|
* Create a new S3 bucket.
|
|
35276
35716
|
*
|
|
35277
35717
|
* Uses AWS SDK CreateBucketCommand. The AWS SDK automatically adds LocationConstraint
|
|
@@ -35920,6 +36360,232 @@ var objectRouter = {
|
|
|
35920
36360
|
return bulkDeleteItems(s3, containerName, uniqueVersions, ctx.req.signal, "delete versions");
|
|
35921
36361
|
}),
|
|
35922
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
|
+
/**
|
|
35923
36589
|
* Create a folder (zero-byte object with key ending in "/").
|
|
35924
36590
|
*
|
|
35925
36591
|
* In S3, folders are virtual - they're just zero-byte objects with keys ending in "/".
|
|
@@ -36688,7 +37354,6 @@ var ec2CredentialRouter = {
|
|
|
36688
37354
|
|
|
36689
37355
|
// src/server/Storage/routers/ceph/versioningRouter.ts
|
|
36690
37356
|
var import_client_s34 = require("@aws-sdk/client-s3");
|
|
36691
|
-
var import_zod17 = require("zod");
|
|
36692
37357
|
|
|
36693
37358
|
// src/server/Storage/types/versioning.ts
|
|
36694
37359
|
var import_zod16 = require("zod");
|
|
@@ -36761,6 +37426,72 @@ var listObjectVersionsInputSchema = projectScopedInputSchema.extend({
|
|
|
36761
37426
|
bucket: import_zod16.z.string().min(1),
|
|
36762
37427
|
key: import_zod16.z.string().min(1)
|
|
36763
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");
|
|
36764
37495
|
|
|
36765
37496
|
// src/server/Storage/routers/ceph/versioningRouter.ts
|
|
36766
37497
|
var versioningRouter = {
|
|
@@ -37006,104 +37737,144 @@ var versioningRouter = {
|
|
|
37006
37737
|
/**
|
|
37007
37738
|
* Check if folders contain deleted files (files with delete markers) or if the folder marker itself is deleted.
|
|
37008
37739
|
*
|
|
37009
|
-
*
|
|
37010
|
-
*
|
|
37011
|
-
*
|
|
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).
|
|
37012
37746
|
*
|
|
37013
|
-
*
|
|
37014
|
-
*
|
|
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.
|
|
37015
37759
|
*
|
|
37016
37760
|
* @throws TRPCError NOT_FOUND - bucket does not exist
|
|
37017
37761
|
* @throws TRPCError FORBIDDEN - no credentials or access denied
|
|
37018
37762
|
*/
|
|
37019
|
-
checkDeletedContent: cephProtectedProcedure.input(
|
|
37020
|
-
project_id: import_zod17.z.string(),
|
|
37021
|
-
bucket: import_zod17.z.string().min(1),
|
|
37022
|
-
folders: import_zod17.z.array(import_zod17.z.string()).max(100)
|
|
37023
|
-
})).query(async ({ ctx, input }) => {
|
|
37763
|
+
checkDeletedContent: cephProtectedProcedure.input(checkDeletedContentInputSchema).query(async ({ ctx, input }) => {
|
|
37024
37764
|
const s3 = ctx.getCephClient();
|
|
37025
|
-
|
|
37026
|
-
|
|
37027
|
-
|
|
37028
|
-
|
|
37029
|
-
|
|
37030
|
-
|
|
37031
|
-
|
|
37032
|
-
|
|
37033
|
-
|
|
37034
|
-
|
|
37035
|
-
|
|
37036
|
-
|
|
37037
|
-
|
|
37038
|
-
|
|
37039
|
-
|
|
37040
|
-
|
|
37041
|
-
|
|
37042
|
-
|
|
37043
|
-
|
|
37044
|
-
|
|
37045
|
-
|
|
37046
|
-
|
|
37047
|
-
|
|
37048
|
-
|
|
37049
|
-
|
|
37050
|
-
|
|
37051
|
-
|
|
37052
|
-
|
|
37053
|
-
|
|
37054
|
-
|
|
37055
|
-
|
|
37056
|
-
|
|
37057
|
-
|
|
37058
|
-
|
|
37059
|
-
|
|
37060
|
-
|
|
37061
|
-
|
|
37062
|
-
}
|
|
37063
|
-
}
|
|
37064
|
-
}
|
|
37065
|
-
if (!hasDeletedNestedObjects) {
|
|
37066
|
-
const deleteMarkersExcludingFolder = response.DeleteMarkers?.filter((dm) => dm.Key !== folderPrefix && dm.IsLatest === true) ?? [];
|
|
37067
|
-
hasDeletedNestedObjects = deleteMarkersExcludingFolder.length > 0;
|
|
37068
|
-
}
|
|
37069
|
-
if (hasDeletedNestedObjects && isFolderMarkerDeleted && folderMarkerVersionId) {
|
|
37070
|
-
break;
|
|
37071
|
-
}
|
|
37072
|
-
if (!response.IsTruncated || !response.NextKeyMarker) {
|
|
37073
|
-
break;
|
|
37074
|
-
}
|
|
37075
|
-
keyMarker = response.NextKeyMarker;
|
|
37076
|
-
versionIdMarker = response.NextVersionIdMarker;
|
|
37077
|
-
} while (true);
|
|
37078
|
-
return {
|
|
37079
|
-
prefix: folderPrefix,
|
|
37080
|
-
hasDeletedContent: isFolderMarkerDeleted || hasDeletedNestedObjects,
|
|
37081
|
-
isFolderDeleted: isFolderMarkerDeleted,
|
|
37082
|
-
folderDeleteMarkerVersionId,
|
|
37083
|
-
folderMarkerVersionId
|
|
37084
|
-
};
|
|
37085
|
-
} catch {
|
|
37086
|
-
return {
|
|
37087
|
-
prefix: folderPrefix,
|
|
37088
|
-
hasDeletedContent: false,
|
|
37089
|
-
isFolderDeleted: false
|
|
37090
|
-
};
|
|
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;
|
|
37091
37802
|
}
|
|
37092
|
-
|
|
37093
|
-
|
|
37094
|
-
|
|
37095
|
-
|
|
37096
|
-
|
|
37097
|
-
|
|
37098
|
-
|
|
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;
|
|
37099
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
|
+
}));
|
|
37100
37871
|
})
|
|
37101
37872
|
};
|
|
37102
37873
|
|
|
37103
37874
|
// src/server/Storage/routers/ceph/bucketPolicyRouter.ts
|
|
37104
37875
|
var import_client_s35 = require("@aws-sdk/client-s3");
|
|
37105
37876
|
var import_server16 = require("@trpc/server");
|
|
37106
|
-
var
|
|
37877
|
+
var import_zod17 = require("zod");
|
|
37107
37878
|
var policySetRateLimits = /* @__PURE__ */ new Map();
|
|
37108
37879
|
function checkPolicySetRateLimit(bucketName, projectId) {
|
|
37109
37880
|
const key = `${projectId}:${bucketName}`;
|
|
@@ -37139,12 +37910,12 @@ function validateResourceARNsMatchBucket(policy, bucketName) {
|
|
|
37139
37910
|
statement.Resource
|
|
37140
37911
|
];
|
|
37141
37912
|
for (const resource of resources) {
|
|
37142
|
-
const arnRegex = /^arn:aws:s3
|
|
37913
|
+
const arnRegex = /^arn:aws:s3::[^:/]*:([^/]+)/;
|
|
37143
37914
|
const match = resource.match(arnRegex);
|
|
37144
37915
|
if (!match) {
|
|
37145
37916
|
throw new import_server16.TRPCError({
|
|
37146
37917
|
code: "BAD_REQUEST",
|
|
37147
|
-
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}`
|
|
37148
37919
|
});
|
|
37149
37920
|
}
|
|
37150
37921
|
if (match[1] !== bucketName) {
|
|
@@ -37179,12 +37950,22 @@ function validatePolicySemantics(policy) {
|
|
|
37179
37950
|
}
|
|
37180
37951
|
}
|
|
37181
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");
|
|
37182
37962
|
var bucketPolicyRouter = {
|
|
37183
37963
|
/**
|
|
37184
37964
|
* Get the current bucket policy.
|
|
37185
37965
|
*
|
|
37186
|
-
* Returns the policy as both a parsed object and raw JSON string.
|
|
37187
|
-
*
|
|
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.
|
|
37188
37969
|
*
|
|
37189
37970
|
* @throws TRPCError NOT_FOUND - bucket does not exist
|
|
37190
37971
|
* @throws TRPCError FORBIDDEN - no credentials or access denied
|
|
@@ -37203,10 +37984,8 @@ var bucketPolicyRouter = {
|
|
|
37203
37984
|
policyText: null
|
|
37204
37985
|
};
|
|
37205
37986
|
}
|
|
37206
|
-
const policyObj = JSON.parse(policyText);
|
|
37207
|
-
const policy = bucketPolicyDocumentSchema.parse(policyObj);
|
|
37208
37987
|
return {
|
|
37209
|
-
policy,
|
|
37988
|
+
policy: parseStoredPolicy(policyText),
|
|
37210
37989
|
policyText
|
|
37211
37990
|
};
|
|
37212
37991
|
} catch (error) {
|
|
@@ -37268,7 +38047,7 @@ var bucketPolicyRouter = {
|
|
|
37268
38047
|
message: "Invalid JSON format"
|
|
37269
38048
|
});
|
|
37270
38049
|
}
|
|
37271
|
-
if (error instanceof
|
|
38050
|
+
if (error instanceof import_zod17.z.ZodError) {
|
|
37272
38051
|
const zodError = error;
|
|
37273
38052
|
const firstError = zodError.issues?.[0];
|
|
37274
38053
|
if (!firstError) {
|
|
@@ -37915,40 +38694,40 @@ var buildObjectStorageRouters = /* @__PURE__ */ __name((policyDir) => ({
|
|
|
37915
38694
|
}), "buildObjectStorageRouters");
|
|
37916
38695
|
|
|
37917
38696
|
// src/server/Project/routers/projectRouter.ts
|
|
37918
|
-
var
|
|
38697
|
+
var import_zod19 = require("zod");
|
|
37919
38698
|
var import_server19 = require("@trpc/server");
|
|
37920
38699
|
|
|
37921
38700
|
// src/server/Project/types/models.ts
|
|
37922
|
-
var
|
|
37923
|
-
var baseProjectSchema =
|
|
37924
|
-
id:
|
|
37925
|
-
name:
|
|
37926
|
-
enabled:
|
|
37927
|
-
domain_id:
|
|
37928
|
-
parent_id:
|
|
37929
|
-
is_domain:
|
|
37930
|
-
tags:
|
|
37931
|
-
options:
|
|
37932
|
-
description:
|
|
37933
|
-
links:
|
|
37934
|
-
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()
|
|
37935
38714
|
}).optional()
|
|
37936
38715
|
});
|
|
37937
38716
|
var projectSchema = baseProjectSchema.extend({
|
|
37938
|
-
domain_name:
|
|
37939
|
-
parents:
|
|
38717
|
+
domain_name: import_zod18.z.string().optional(),
|
|
38718
|
+
parents: import_zod18.z.array(import_zod18.z.object({
|
|
37940
38719
|
project: baseProjectSchema
|
|
37941
38720
|
})).optional()
|
|
37942
38721
|
});
|
|
37943
|
-
var projectResponseSchema =
|
|
38722
|
+
var projectResponseSchema = import_zod18.z.object({
|
|
37944
38723
|
project: projectSchema
|
|
37945
38724
|
});
|
|
37946
|
-
var projectsResponseSchema =
|
|
37947
|
-
projects:
|
|
37948
|
-
links:
|
|
37949
|
-
self:
|
|
37950
|
-
previous:
|
|
37951
|
-
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()
|
|
37952
38731
|
}).optional()
|
|
37953
38732
|
});
|
|
37954
38733
|
|
|
@@ -38035,10 +38814,10 @@ var projectRouter = {
|
|
|
38035
38814
|
return void 0;
|
|
38036
38815
|
}
|
|
38037
38816
|
const domainsData = domainsResponse ? await domainsResponse.json().catch(() => null) : null;
|
|
38038
|
-
const domainsResponseSchema =
|
|
38039
|
-
domains:
|
|
38040
|
-
id:
|
|
38041
|
-
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()
|
|
38042
38821
|
}))
|
|
38043
38822
|
});
|
|
38044
38823
|
const parsedDomains = domainsResponseSchema.safeParse(domainsData);
|
|
@@ -38058,8 +38837,8 @@ var projectRouter = {
|
|
|
38058
38837
|
* filtered by the optional search term. Uses the OpenStack /v3/auth/projects
|
|
38059
38838
|
* endpoint which works with any valid token.
|
|
38060
38839
|
*/
|
|
38061
|
-
searchProjects: protectedProcedure.input(
|
|
38062
|
-
search:
|
|
38840
|
+
searchProjects: protectedProcedure.input(import_zod19.z.object({
|
|
38841
|
+
search: import_zod19.z.string().optional()
|
|
38063
38842
|
}).optional()).query(async ({ ctx, input }) => {
|
|
38064
38843
|
if (!ctx.openstack) {
|
|
38065
38844
|
throw new import_server19.TRPCError({
|
|
@@ -38089,8 +38868,65 @@ var projectRouter = {
|
|
|
38089
38868
|
projects.sort((a, b) => a.name.localeCompare(b.name));
|
|
38090
38869
|
return projects;
|
|
38091
38870
|
}),
|
|
38092
|
-
|
|
38093
|
-
|
|
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()
|
|
38094
38930
|
})).query(async ({ ctx, input }) => {
|
|
38095
38931
|
if (!ctx.openstack) {
|
|
38096
38932
|
throw new import_server19.TRPCError({
|
|
@@ -38200,16 +39036,16 @@ __name(validateOpenstackService, "validateOpenstackService");
|
|
|
38200
39036
|
var import_signal_openstack7 = __toESM(require_esm());
|
|
38201
39037
|
|
|
38202
39038
|
// src/server/Network/types/floatingIp.ts
|
|
38203
|
-
var
|
|
39039
|
+
var import_zod21 = require("zod");
|
|
38204
39040
|
|
|
38205
39041
|
// src/server/Network/types/index.ts
|
|
38206
|
-
var
|
|
38207
|
-
var ISO8601TimestampSchema =
|
|
38208
|
-
var SortDirSchema =
|
|
39042
|
+
var import_zod20 = require("zod");
|
|
39043
|
+
var ISO8601TimestampSchema = import_zod20.z.string().brand("ISO8601Timestamp");
|
|
39044
|
+
var SortDirSchema = import_zod20.z.enum([
|
|
38209
39045
|
"asc",
|
|
38210
39046
|
"desc"
|
|
38211
39047
|
]);
|
|
38212
|
-
var NetworkPortStatusSchema =
|
|
39048
|
+
var NetworkPortStatusSchema = import_zod20.z.enum([
|
|
38213
39049
|
"ACTIVE",
|
|
38214
39050
|
"DOWN",
|
|
38215
39051
|
"BUILD",
|
|
@@ -38217,153 +39053,153 @@ var NetworkPortStatusSchema = import_zod21.z.enum([
|
|
|
38217
39053
|
]);
|
|
38218
39054
|
|
|
38219
39055
|
// src/server/Network/types/floatingIp.ts
|
|
38220
|
-
var FloatingIpStatusSchema =
|
|
39056
|
+
var FloatingIpStatusSchema = import_zod21.z.enum([
|
|
38221
39057
|
"ACTIVE",
|
|
38222
39058
|
"DOWN",
|
|
38223
39059
|
"ERROR"
|
|
38224
39060
|
]);
|
|
38225
|
-
var PortDetailsSchema =
|
|
39061
|
+
var PortDetailsSchema = import_zod21.z.object({
|
|
38226
39062
|
/** The status of the port */
|
|
38227
39063
|
status: FloatingIpStatusSchema,
|
|
38228
39064
|
/** The name of the port */
|
|
38229
|
-
name:
|
|
39065
|
+
name: import_zod21.z.string(),
|
|
38230
39066
|
/** Administrative state of the port (true = UP, false = DOWN) */
|
|
38231
|
-
admin_state_up:
|
|
39067
|
+
admin_state_up: import_zod21.z.boolean(),
|
|
38232
39068
|
/** The ID of the network the port is attached to */
|
|
38233
|
-
network_id:
|
|
39069
|
+
network_id: import_zod21.z.string(),
|
|
38234
39070
|
/** The owner of the port device */
|
|
38235
|
-
device_owner:
|
|
39071
|
+
device_owner: import_zod21.z.string(),
|
|
38236
39072
|
/** The MAC address of the port */
|
|
38237
|
-
mac_address:
|
|
39073
|
+
mac_address: import_zod21.z.string(),
|
|
38238
39074
|
/** The ID of the device the port is attached to */
|
|
38239
|
-
device_id:
|
|
39075
|
+
device_id: import_zod21.z.string()
|
|
38240
39076
|
});
|
|
38241
|
-
var PortForwardingSchema =
|
|
39077
|
+
var PortForwardingSchema = import_zod21.z.object({
|
|
38242
39078
|
/** Port forwarding protocol */
|
|
38243
|
-
protocol:
|
|
39079
|
+
protocol: import_zod21.z.enum([
|
|
38244
39080
|
"tcp",
|
|
38245
39081
|
"udp"
|
|
38246
39082
|
]),
|
|
38247
39083
|
/** The internal fixed IP address associated with the port forwarding */
|
|
38248
|
-
internal_ip_address:
|
|
39084
|
+
internal_ip_address: import_zod21.z.string(),
|
|
38249
39085
|
/** The internal port number or starting port of the range */
|
|
38250
|
-
internal_port:
|
|
39086
|
+
internal_port: import_zod21.z.number().optional(),
|
|
38251
39087
|
/** The internal port range (e.g., "1024:2048") */
|
|
38252
|
-
internal_port_range:
|
|
39088
|
+
internal_port_range: import_zod21.z.string().optional(),
|
|
38253
39089
|
/** The internal port ID for the port forwarding (requires floating-ip-port-forwarding-detail extension) */
|
|
38254
|
-
internal_port_id:
|
|
39090
|
+
internal_port_id: import_zod21.z.string().optional(),
|
|
38255
39091
|
/** The external port number or starting port of the range */
|
|
38256
|
-
external_port:
|
|
39092
|
+
external_port: import_zod21.z.number().optional(),
|
|
38257
39093
|
/** The external port range (e.g., "8000:9000") */
|
|
38258
|
-
external_port_range:
|
|
39094
|
+
external_port_range: import_zod21.z.string().optional(),
|
|
38259
39095
|
/** The ID of the port forwarding resource */
|
|
38260
|
-
id:
|
|
39096
|
+
id: import_zod21.z.string(),
|
|
38261
39097
|
/** Description of the port forwarding rule (requires floating-ip-port-forwarding-description extension) */
|
|
38262
|
-
description:
|
|
39098
|
+
description: import_zod21.z.string().nullable().optional()
|
|
38263
39099
|
});
|
|
38264
|
-
var FloatingIpSchema =
|
|
39100
|
+
var FloatingIpSchema = import_zod21.z.object({
|
|
38265
39101
|
/** The ID of the router for the floating IP (null if not associated) */
|
|
38266
|
-
router_id:
|
|
39102
|
+
router_id: import_zod21.z.string().nullable(),
|
|
38267
39103
|
/** A human-readable description for the resource */
|
|
38268
|
-
description:
|
|
39104
|
+
description: import_zod21.z.string().nullable().optional(),
|
|
38269
39105
|
/** Whether this is a distributed floating IP (requires floating-ip-distributed extension) */
|
|
38270
|
-
distributed:
|
|
39106
|
+
distributed: import_zod21.z.boolean().optional(),
|
|
38271
39107
|
/** A valid DNS domain (requires dns-integration extension) */
|
|
38272
|
-
dns_domain:
|
|
39108
|
+
dns_domain: import_zod21.z.string().optional(),
|
|
38273
39109
|
/** A valid DNS name (requires dns-integration extension) */
|
|
38274
|
-
dns_name:
|
|
39110
|
+
dns_name: import_zod21.z.string().optional(),
|
|
38275
39111
|
/** Time at which the resource was created (UTC ISO8601 format, requires standard-attr-timestamp extension) */
|
|
38276
39112
|
created_at: ISO8601TimestampSchema.optional(),
|
|
38277
39113
|
/** Time at which the resource was last updated (UTC ISO8601 format, requires standard-attr-timestamp extension) */
|
|
38278
39114
|
updated_at: ISO8601TimestampSchema.optional(),
|
|
38279
39115
|
/** The revision number of the resource (set by server, for concurrency control) */
|
|
38280
|
-
revision_number:
|
|
39116
|
+
revision_number: import_zod21.z.number(),
|
|
38281
39117
|
/** The ID of the project that owns the floating IP */
|
|
38282
|
-
project_id:
|
|
39118
|
+
project_id: import_zod21.z.string(),
|
|
38283
39119
|
/** The ID of the tenant (deprecated, use project_id) */
|
|
38284
|
-
tenant_id:
|
|
39120
|
+
tenant_id: import_zod21.z.string(),
|
|
38285
39121
|
/** The ID of the network associated with the floating IP */
|
|
38286
|
-
floating_network_id:
|
|
39122
|
+
floating_network_id: import_zod21.z.string(),
|
|
38287
39123
|
/** The fixed IP address associated with the floating IP (null if not associated) */
|
|
38288
|
-
fixed_ip_address:
|
|
39124
|
+
fixed_ip_address: import_zod21.z.string().nullable(),
|
|
38289
39125
|
/** The floating IP address */
|
|
38290
|
-
floating_ip_address:
|
|
39126
|
+
floating_ip_address: import_zod21.z.string(),
|
|
38291
39127
|
/** The ID of the port associated with the floating IP (null if not associated) */
|
|
38292
|
-
port_id:
|
|
39128
|
+
port_id: import_zod21.z.string().nullable(),
|
|
38293
39129
|
/** The ID of the floating IP address */
|
|
38294
|
-
id:
|
|
39130
|
+
id: import_zod21.z.string(),
|
|
38295
39131
|
/** The status of the floating IP */
|
|
38296
39132
|
status: FloatingIpStatusSchema,
|
|
38297
39133
|
/** The information of the associated port (null if not associated, requires fip-port-details extension) */
|
|
38298
39134
|
port_details: PortDetailsSchema.nullable().optional(),
|
|
38299
39135
|
/** The list of tags on the resource (requires standard-attr-tag extension) */
|
|
38300
|
-
tags:
|
|
39136
|
+
tags: import_zod21.z.array(import_zod21.z.string()).optional(),
|
|
38301
39137
|
/** The associated port forwarding resources for the floating IP (requires expose-port-forwarding-in-fip extension) */
|
|
38302
|
-
port_forwardings:
|
|
39138
|
+
port_forwardings: import_zod21.z.array(PortForwardingSchema).nullish(),
|
|
38303
39139
|
/** The ID of the QoS policy of the network where this floating IP is plugged (requires qos-fip extension) */
|
|
38304
|
-
qos_network_policy_id:
|
|
39140
|
+
qos_network_policy_id: import_zod21.z.string().optional(),
|
|
38305
39141
|
/** The ID of the QoS policy associated with the floating IP (requires qos extension) */
|
|
38306
|
-
qos_policy_id:
|
|
38307
|
-
});
|
|
38308
|
-
var FloatingIpIdInputSchema =
|
|
38309
|
-
project_id:
|
|
38310
|
-
floatingip_id:
|
|
38311
|
-
});
|
|
38312
|
-
var FloatingIpCreateRequestSchema =
|
|
38313
|
-
project_id:
|
|
38314
|
-
floating_network_id:
|
|
38315
|
-
fixed_ip_address:
|
|
38316
|
-
floating_ip_address:
|
|
38317
|
-
port_id:
|
|
38318
|
-
subnet_id:
|
|
38319
|
-
distributed:
|
|
38320
|
-
description:
|
|
38321
|
-
dns_domain:
|
|
38322
|
-
dns_name:
|
|
38323
|
-
qos_policy_id:
|
|
38324
|
-
});
|
|
38325
|
-
var FloatingIpUpdateRequestSchema =
|
|
38326
|
-
project_id:
|
|
38327
|
-
floatingip_id:
|
|
38328
|
-
port_id:
|
|
38329
|
-
fixed_ip_address:
|
|
38330
|
-
description:
|
|
38331
|
-
distributed:
|
|
38332
|
-
});
|
|
38333
|
-
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({
|
|
38334
39170
|
floatingip: FloatingIpSchema
|
|
38335
39171
|
});
|
|
38336
|
-
var FloatingIpListResponseSchema =
|
|
39172
|
+
var FloatingIpListResponseSchema = import_zod21.z.object({
|
|
38337
39173
|
/** A list of floating IP objects */
|
|
38338
|
-
floatingips:
|
|
39174
|
+
floatingips: import_zod21.z.array(FloatingIpSchema)
|
|
38339
39175
|
});
|
|
38340
|
-
var FloatingIpQueryParametersSchema =
|
|
39176
|
+
var FloatingIpQueryParametersSchema = import_zod21.z.object({
|
|
38341
39177
|
/** Project ID for rescoping (required for projectScopedProcedure) */
|
|
38342
|
-
project_id:
|
|
39178
|
+
project_id: import_zod21.z.string(),
|
|
38343
39179
|
/** Filter by the ID of the floating IP */
|
|
38344
|
-
id:
|
|
39180
|
+
id: import_zod21.z.string().optional(),
|
|
38345
39181
|
/** Filter by the ID of the router for the floating IP */
|
|
38346
|
-
router_id:
|
|
39182
|
+
router_id: import_zod21.z.string().nullable().optional(),
|
|
38347
39183
|
/** Filter by the status of the floating IP */
|
|
38348
39184
|
status: FloatingIpStatusSchema.optional(),
|
|
38349
39185
|
/** Filter by the ID of the project that owns the resource */
|
|
38350
|
-
tenant_id:
|
|
39186
|
+
tenant_id: import_zod21.z.string().optional(),
|
|
38351
39187
|
/** Filter by the revision number of the resource */
|
|
38352
|
-
revision_number:
|
|
39188
|
+
revision_number: import_zod21.z.number().optional(),
|
|
38353
39189
|
/** Filter by the human-readable description of the resource */
|
|
38354
|
-
description:
|
|
39190
|
+
description: import_zod21.z.string().nullable().optional(),
|
|
38355
39191
|
/** Filter by the ID of the network associated with the floating IP */
|
|
38356
|
-
floating_network_id:
|
|
39192
|
+
floating_network_id: import_zod21.z.string().optional(),
|
|
38357
39193
|
/** Filter by the fixed IP address associated with the floating IP */
|
|
38358
|
-
fixed_ip_address:
|
|
39194
|
+
fixed_ip_address: import_zod21.z.string().optional(),
|
|
38359
39195
|
/** Filter by the floating IP address */
|
|
38360
|
-
floating_ip_address:
|
|
39196
|
+
floating_ip_address: import_zod21.z.string().optional(),
|
|
38361
39197
|
/** Filter by the ID of a port associated with the floating IP */
|
|
38362
|
-
port_id:
|
|
39198
|
+
port_id: import_zod21.z.string().nullable().optional(),
|
|
38363
39199
|
/** Sort direction (asc or desc) */
|
|
38364
39200
|
sort_dir: SortDirSchema.optional(),
|
|
38365
39201
|
/** Sort key - valid keys: fixed_ip_address, floating_ip_address, floating_network_id, id, router_id, status, tenant_id, project_id */
|
|
38366
|
-
sort_key:
|
|
39202
|
+
sort_key: import_zod21.z.enum([
|
|
38367
39203
|
"fixed_ip_address",
|
|
38368
39204
|
"floating_ip_address",
|
|
38369
39205
|
"floating_network_id",
|
|
@@ -38374,73 +39210,73 @@ var FloatingIpQueryParametersSchema = import_zod22.z.object({
|
|
|
38374
39210
|
"project_id"
|
|
38375
39211
|
]).optional(),
|
|
38376
39212
|
/** Filter by tags (comma-separated, resources must match all tags) - requires standard-attr-tag extension */
|
|
38377
|
-
tags:
|
|
39213
|
+
tags: import_zod21.z.array(import_zod21.z.string()).optional(),
|
|
38378
39214
|
/** Filter by tags with OR logic (comma-separated, resources match any tag) */
|
|
38379
|
-
"tags-any":
|
|
39215
|
+
"tags-any": import_zod21.z.string().optional(),
|
|
38380
39216
|
/** Exclude resources with these tags (comma-separated, resources must match all excluded tags) */
|
|
38381
|
-
"not-tags":
|
|
39217
|
+
"not-tags": import_zod21.z.string().optional(),
|
|
38382
39218
|
/** Exclude resources with these tags using OR logic (comma-separated, resources match any excluded tag) */
|
|
38383
|
-
"not-tags-any":
|
|
39219
|
+
"not-tags-any": import_zod21.z.string().optional(),
|
|
38384
39220
|
/** Specific fields to return (can be repeated) */
|
|
38385
|
-
fields:
|
|
38386
|
-
|
|
38387
|
-
|
|
39221
|
+
fields: import_zod21.z.union([
|
|
39222
|
+
import_zod21.z.string(),
|
|
39223
|
+
import_zod21.z.array(import_zod21.z.string())
|
|
38388
39224
|
]).optional(),
|
|
38389
39225
|
/** Pagination limit */
|
|
38390
|
-
limit:
|
|
39226
|
+
limit: import_zod21.z.number().optional(),
|
|
38391
39227
|
/** Pagination marker (ID of the last item in the previous list) */
|
|
38392
|
-
marker:
|
|
39228
|
+
marker: import_zod21.z.string().optional(),
|
|
38393
39229
|
/** Pagination direction (false = ascending, true = descending) */
|
|
38394
|
-
page_reverse:
|
|
39230
|
+
page_reverse: import_zod21.z.boolean().optional(),
|
|
38395
39231
|
// BFF-side search (filtered in BFF layer, not sent to OpenStack)
|
|
38396
|
-
searchTerm:
|
|
38397
|
-
});
|
|
38398
|
-
var ExternalNetworksQuerySchema =
|
|
38399
|
-
project_id:
|
|
38400
|
-
"router:external":
|
|
38401
|
-
});
|
|
38402
|
-
var ExternalNetworkSchema =
|
|
38403
|
-
id:
|
|
38404
|
-
name:
|
|
38405
|
-
project_id:
|
|
38406
|
-
"router:external":
|
|
38407
|
-
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(),
|
|
38408
39244
|
status: NetworkPortStatusSchema,
|
|
38409
39245
|
updated_at: ISO8601TimestampSchema.optional(),
|
|
38410
|
-
is_default:
|
|
39246
|
+
is_default: import_zod21.z.boolean().optional()
|
|
38411
39247
|
});
|
|
38412
|
-
var ExternalNetworksResponseSchema =
|
|
38413
|
-
networks:
|
|
39248
|
+
var ExternalNetworksResponseSchema = import_zod21.z.object({
|
|
39249
|
+
networks: import_zod21.z.array(ExternalNetworkSchema)
|
|
38414
39250
|
});
|
|
38415
|
-
var DnsDomainSchema =
|
|
38416
|
-
id:
|
|
38417
|
-
name:
|
|
39251
|
+
var DnsDomainSchema = import_zod21.z.object({
|
|
39252
|
+
id: import_zod21.z.string(),
|
|
39253
|
+
name: import_zod21.z.string()
|
|
38418
39254
|
});
|
|
38419
|
-
var DnsDomainResponseSchema =
|
|
38420
|
-
zones:
|
|
39255
|
+
var DnsDomainResponseSchema = import_zod21.z.object({
|
|
39256
|
+
zones: import_zod21.z.array(DnsDomainSchema)
|
|
38421
39257
|
});
|
|
38422
|
-
var AvailablePortsQuerySchema =
|
|
38423
|
-
project_id:
|
|
39258
|
+
var AvailablePortsQuerySchema = import_zod21.z.object({
|
|
39259
|
+
project_id: import_zod21.z.string(),
|
|
38424
39260
|
/** Only ACTIVE ports are eligible for floating IP association */
|
|
38425
|
-
status:
|
|
39261
|
+
status: import_zod21.z.literal("ACTIVE").default("ACTIVE"),
|
|
38426
39262
|
/** Only administratively UP ports are eligible for floating IP association */
|
|
38427
|
-
admin_state_up:
|
|
39263
|
+
admin_state_up: import_zod21.z.literal(true).default(true)
|
|
38428
39264
|
});
|
|
38429
|
-
var AvailablePortSchema =
|
|
39265
|
+
var AvailablePortSchema = import_zod21.z.object({
|
|
38430
39266
|
/** The ID of the port */
|
|
38431
|
-
id:
|
|
39267
|
+
id: import_zod21.z.string(),
|
|
38432
39268
|
/** Human-readable name of the port */
|
|
38433
|
-
name:
|
|
39269
|
+
name: import_zod21.z.string().nullable().optional(),
|
|
38434
39270
|
/** List of fixed IPs assigned to the port, each entry pairs an IP address with its subnet. */
|
|
38435
|
-
fixed_ips:
|
|
39271
|
+
fixed_ips: import_zod21.z.array(import_zod21.z.object({
|
|
38436
39272
|
/** The fixed IP address assigned to the port */
|
|
38437
|
-
ip_address:
|
|
39273
|
+
ip_address: import_zod21.z.string(),
|
|
38438
39274
|
/** The ID of the subnet the IP belongs to */
|
|
38439
|
-
subnet_id:
|
|
39275
|
+
subnet_id: import_zod21.z.string().optional()
|
|
38440
39276
|
})).optional()
|
|
38441
39277
|
});
|
|
38442
|
-
var AvailablePortsResponseSchema =
|
|
38443
|
-
ports:
|
|
39278
|
+
var AvailablePortsResponseSchema = import_zod21.z.object({
|
|
39279
|
+
ports: import_zod21.z.array(AvailablePortSchema)
|
|
38444
39280
|
});
|
|
38445
39281
|
|
|
38446
39282
|
// src/server/Network/helpers/errorHandling.ts
|
|
@@ -38548,6 +39384,7 @@ var floatingIpRouter = {
|
|
|
38548
39384
|
const data = await response.json();
|
|
38549
39385
|
const { floatingips } = parseOrThrow(FloatingIpListResponseSchema, data, "floatingIpRouter.list");
|
|
38550
39386
|
return filterBySearchParams(floatingips, searchTerm, [
|
|
39387
|
+
"id",
|
|
38551
39388
|
"description",
|
|
38552
39389
|
"floating_ip_address",
|
|
38553
39390
|
"fixed_ip_address",
|
|
@@ -38701,112 +39538,112 @@ var floatingIpRouter = {
|
|
|
38701
39538
|
};
|
|
38702
39539
|
|
|
38703
39540
|
// src/server/Network/types/securityGroup.ts
|
|
38704
|
-
var
|
|
38705
|
-
var securityGroupRuleSchema =
|
|
38706
|
-
id:
|
|
38707
|
-
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([
|
|
38708
39545
|
"ingress",
|
|
38709
39546
|
"egress"
|
|
38710
39547
|
]).optional(),
|
|
38711
|
-
ethertype:
|
|
39548
|
+
ethertype: import_zod22.z.enum([
|
|
38712
39549
|
"IPv4",
|
|
38713
39550
|
"IPv6"
|
|
38714
39551
|
]).optional(),
|
|
38715
|
-
description:
|
|
38716
|
-
security_group_id:
|
|
38717
|
-
protocol:
|
|
38718
|
-
port_range_min:
|
|
38719
|
-
port_range_max:
|
|
38720
|
-
remote_ip_prefix:
|
|
38721
|
-
remote_group_id:
|
|
38722
|
-
remote_address_group_id:
|
|
38723
|
-
tenant_id:
|
|
38724
|
-
project_id:
|
|
38725
|
-
revision_number:
|
|
38726
|
-
tags:
|
|
38727
|
-
created_at:
|
|
38728
|
-
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()
|
|
38729
39566
|
});
|
|
38730
|
-
var securityGroupSchema =
|
|
38731
|
-
id:
|
|
38732
|
-
name:
|
|
38733
|
-
description:
|
|
38734
|
-
tenant_id:
|
|
38735
|
-
project_id:
|
|
38736
|
-
stateful:
|
|
38737
|
-
shared:
|
|
38738
|
-
tags:
|
|
38739
|
-
security_group_rules:
|
|
38740
|
-
revision_number:
|
|
38741
|
-
created_at:
|
|
38742
|
-
updated_at:
|
|
38743
|
-
});
|
|
38744
|
-
var securityGroupsResponseSchema =
|
|
38745
|
-
security_groups:
|
|
38746
|
-
});
|
|
38747
|
-
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({
|
|
38748
39585
|
security_group: securityGroupSchema
|
|
38749
39586
|
});
|
|
38750
|
-
var securityGroupRuleResponseSchema =
|
|
39587
|
+
var securityGroupRuleResponseSchema = import_zod22.z.object({
|
|
38751
39588
|
security_group_rule: securityGroupRuleSchema
|
|
38752
39589
|
});
|
|
38753
39590
|
var listSecurityGroupsInputSchema = projectScopedInputSchema.extend({
|
|
38754
39591
|
// Sorting
|
|
38755
|
-
sort_key:
|
|
39592
|
+
sort_key: import_zod22.z.string().optional(),
|
|
38756
39593
|
sort_dir: SortDirSchema.optional(),
|
|
38757
39594
|
// Basic filtering
|
|
38758
|
-
name:
|
|
38759
|
-
description:
|
|
38760
|
-
tenant_id:
|
|
38761
|
-
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(),
|
|
38762
39599
|
// Tag-based filtering (string values follow Neutron semantics)
|
|
38763
|
-
tags:
|
|
38764
|
-
tags_any:
|
|
38765
|
-
not_tags:
|
|
38766
|
-
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(),
|
|
38767
39604
|
// Filtered in the BFF layer, not forwarded to Neutron
|
|
38768
|
-
stateful:
|
|
39605
|
+
stateful: import_zod22.z.boolean().optional(),
|
|
38769
39606
|
// BFF-side search (filtered in BFF layer, not sent to OpenStack)
|
|
38770
|
-
searchTerm:
|
|
39607
|
+
searchTerm: import_zod22.z.string().optional()
|
|
38771
39608
|
});
|
|
38772
39609
|
var getSecurityGroupByIdInputSchema = projectScopedInputSchema.extend({
|
|
38773
|
-
securityGroupId:
|
|
39610
|
+
securityGroupId: import_zod22.z.string()
|
|
38774
39611
|
});
|
|
38775
39612
|
var createSecurityGroupInputSchema = projectScopedInputSchema.extend({
|
|
38776
|
-
name:
|
|
38777
|
-
description:
|
|
38778
|
-
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()
|
|
38779
39616
|
});
|
|
38780
39617
|
var deleteSecurityGroupInputSchema = projectScopedInputSchema.extend({
|
|
38781
|
-
securityGroupId:
|
|
39618
|
+
securityGroupId: import_zod22.z.string()
|
|
38782
39619
|
});
|
|
38783
39620
|
var updateSecurityGroupInputSchema = projectScopedInputSchema.extend({
|
|
38784
|
-
securityGroupId:
|
|
38785
|
-
name:
|
|
38786
|
-
description:
|
|
38787
|
-
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()
|
|
38788
39625
|
});
|
|
38789
39626
|
var deleteSecurityGroupRuleInputSchema = projectScopedInputSchema.extend({
|
|
38790
|
-
ruleId:
|
|
39627
|
+
ruleId: import_zod22.z.string()
|
|
38791
39628
|
});
|
|
38792
39629
|
var createSecurityGroupRuleInputSchema = projectScopedInputSchema.extend({
|
|
38793
|
-
security_group_id:
|
|
38794
|
-
direction:
|
|
39630
|
+
security_group_id: import_zod22.z.string(),
|
|
39631
|
+
direction: import_zod22.z.enum([
|
|
38795
39632
|
"ingress",
|
|
38796
39633
|
"egress"
|
|
38797
39634
|
]),
|
|
38798
|
-
ethertype:
|
|
39635
|
+
ethertype: import_zod22.z.enum([
|
|
38799
39636
|
"IPv4",
|
|
38800
39637
|
"IPv6"
|
|
38801
39638
|
]).default("IPv4"),
|
|
38802
|
-
description:
|
|
38803
|
-
protocol:
|
|
38804
|
-
port_range_min:
|
|
38805
|
-
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(),
|
|
38806
39643
|
// Transform empty strings to undefined for proper validation
|
|
38807
|
-
remote_ip_prefix:
|
|
38808
|
-
remote_group_id:
|
|
38809
|
-
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)
|
|
38810
39647
|
});
|
|
38811
39648
|
|
|
38812
39649
|
// src/server/Network/helpers/securityGroupHelpers.ts
|
|
@@ -39335,42 +40172,42 @@ var securityGroupRuleRouter = {
|
|
|
39335
40172
|
};
|
|
39336
40173
|
|
|
39337
40174
|
// src/server/Network/types/rbacPolicy.ts
|
|
39338
|
-
var
|
|
39339
|
-
var rbacPolicySchema =
|
|
39340
|
-
id:
|
|
39341
|
-
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([
|
|
39342
40179
|
"qos_policy",
|
|
39343
40180
|
"network",
|
|
39344
40181
|
"security_group"
|
|
39345
40182
|
]),
|
|
39346
|
-
object_id:
|
|
39347
|
-
action:
|
|
40183
|
+
object_id: import_zod23.z.string(),
|
|
40184
|
+
action: import_zod23.z.enum([
|
|
39348
40185
|
"access_as_shared",
|
|
39349
40186
|
"access_as_external"
|
|
39350
40187
|
]),
|
|
39351
|
-
target_tenant:
|
|
39352
|
-
tenant_id:
|
|
39353
|
-
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()
|
|
39354
40191
|
});
|
|
39355
|
-
var rbacPoliciesResponseSchema =
|
|
39356
|
-
rbac_policies:
|
|
40192
|
+
var rbacPoliciesResponseSchema = import_zod23.z.object({
|
|
40193
|
+
rbac_policies: import_zod23.z.array(rbacPolicySchema)
|
|
39357
40194
|
});
|
|
39358
|
-
var rbacPolicyResponseSchema =
|
|
40195
|
+
var rbacPolicyResponseSchema = import_zod23.z.object({
|
|
39359
40196
|
rbac_policy: rbacPolicySchema
|
|
39360
40197
|
});
|
|
39361
40198
|
var listRBACPoliciesForSecurityGroupInputSchema = projectScopedInputSchema.extend({
|
|
39362
|
-
securityGroupId:
|
|
40199
|
+
securityGroupId: import_zod23.z.string()
|
|
39363
40200
|
});
|
|
39364
40201
|
var createRBACPolicyInputSchema = projectScopedInputSchema.extend({
|
|
39365
|
-
securityGroupId:
|
|
39366
|
-
targetTenant:
|
|
40202
|
+
securityGroupId: import_zod23.z.string(),
|
|
40203
|
+
targetTenant: import_zod23.z.string().trim().min(1, "Target project ID is required")
|
|
39367
40204
|
});
|
|
39368
40205
|
var updateRBACPolicyInputSchema = projectScopedInputSchema.extend({
|
|
39369
|
-
policyId:
|
|
39370
|
-
targetTenant:
|
|
40206
|
+
policyId: import_zod23.z.string(),
|
|
40207
|
+
targetTenant: import_zod23.z.string().trim().min(1, "Target project ID is required")
|
|
39371
40208
|
});
|
|
39372
40209
|
var deleteRBACPolicyInputSchema = projectScopedInputSchema.extend({
|
|
39373
|
-
policyId:
|
|
40210
|
+
policyId: import_zod23.z.string()
|
|
39374
40211
|
});
|
|
39375
40212
|
|
|
39376
40213
|
// src/server/Network/helpers/rbacPolicyHelpers.ts
|
|
@@ -40127,7 +40964,7 @@ __name(createContext, "createContext");
|
|
|
40127
40964
|
// src/server/server.ts
|
|
40128
40965
|
var import_path2 = __toESM(require("path"));
|
|
40129
40966
|
var import_node_stream4 = require("stream");
|
|
40130
|
-
var
|
|
40967
|
+
var import_zod24 = require("zod");
|
|
40131
40968
|
|
|
40132
40969
|
// src/server/aurora-fastify-plugins/csrfProtection.ts
|
|
40133
40970
|
var import_csrf_protection = __toESM(require("@fastify/csrf-protection"));
|
|
@@ -40328,7 +41165,7 @@ async function createServer(config) {
|
|
|
40328
41165
|
router: appRouter,
|
|
40329
41166
|
createContext: /* @__PURE__ */ __name((opts) => createContext(opts, contextConfig), "createContext"),
|
|
40330
41167
|
onError: /* @__PURE__ */ __name((err) => {
|
|
40331
|
-
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(",");
|
|
40332
41169
|
}, "onError")
|
|
40333
41170
|
}
|
|
40334
41171
|
});
|