diff --git a/package-lock.json b/package-lock.json index 08a492e19a5ce5d923065fe38ede83948c6e0202..25bd11420b05db19e875c228d3a22c4f07dd992e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,7 +8,7 @@ "name": "proxyapi-ui", "version": "0.0.0", "dependencies": { - "@aws-sdk/client-s3": "^3.131.0", + "@aws-sdk/client-s3": "^3.154.0", "@popperjs/core": "^2.11.5", "bootstrap": "^5.2.0", "bootstrap-icons": "^1.9.1", @@ -180,16 +180,16 @@ "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" }, "node_modules/@aws-sdk/client-s3": { - "version": "3.131.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-s3/-/client-s3-3.131.0.tgz", - "integrity": "sha512-ue6uzk04pRJCJIaU1xKAW7Fx4TJx5n5Dwtycm0H94msj5HpJOfDKPoO9+kbywZywWP7n+eNnzNl/6lMjCSfO4g==", + "version": "3.154.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-s3/-/client-s3-3.154.0.tgz", + "integrity": "sha512-TUkUZUmDuQlh7N6455LNqjuky/iIhjvnISJzl+wMKyQUMX61FPDZMYDG7HXDEQGV7uaAvjaabKfyHVNdoi/SmA==", "dependencies": { "@aws-crypto/sha1-browser": "2.0.0", "@aws-crypto/sha256-browser": "2.0.0", "@aws-crypto/sha256-js": "2.0.0", - "@aws-sdk/client-sts": "3.131.0", + "@aws-sdk/client-sts": "3.154.0", "@aws-sdk/config-resolver": "3.130.0", - "@aws-sdk/credential-provider-node": "3.131.0", + "@aws-sdk/credential-provider-node": "3.154.0", "@aws-sdk/eventstream-serde-browser": "3.127.0", "@aws-sdk/eventstream-serde-config-resolver": "3.127.0", "@aws-sdk/eventstream-serde-node": "3.127.0", @@ -218,15 +218,15 @@ "@aws-sdk/node-http-handler": "3.127.0", "@aws-sdk/protocol-http": "3.127.0", "@aws-sdk/signature-v4-multi-region": "3.130.0", - "@aws-sdk/smithy-client": "3.127.0", + "@aws-sdk/smithy-client": "3.142.0", "@aws-sdk/types": "3.127.0", "@aws-sdk/url-parser": "3.127.0", "@aws-sdk/util-base64-browser": "3.109.0", "@aws-sdk/util-base64-node": "3.55.0", - "@aws-sdk/util-body-length-browser": "3.55.0", + "@aws-sdk/util-body-length-browser": "3.154.0", "@aws-sdk/util-body-length-node": "3.55.0", - "@aws-sdk/util-defaults-mode-browser": "3.127.0", - "@aws-sdk/util-defaults-mode-node": "3.130.0", + "@aws-sdk/util-defaults-mode-browser": "3.142.0", + "@aws-sdk/util-defaults-mode-node": "3.142.0", "@aws-sdk/util-stream-browser": "3.131.0", "@aws-sdk/util-stream-node": "3.129.0", "@aws-sdk/util-user-agent-browser": "3.127.0", @@ -234,7 +234,7 @@ "@aws-sdk/util-utf8-browser": "3.109.0", "@aws-sdk/util-utf8-node": "3.109.0", "@aws-sdk/util-waiter": "3.127.0", - "@aws-sdk/xml-builder": "3.109.0", + "@aws-sdk/xml-builder": "3.142.0", "entities": "2.2.0", "fast-xml-parser": "3.19.0", "tslib": "^2.3.1" @@ -249,9 +249,9 @@ "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" }, "node_modules/@aws-sdk/client-sso": { - "version": "3.131.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.131.0.tgz", - "integrity": "sha512-6fbjqLdVZF7mvGpHjWX5YsqBE/99MilNtGUFlwuf4/KnmYy49V16A6Dltnd43Hu6HVGxJ8caH9nCkIdNp3YZcQ==", + "version": "3.154.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.154.0.tgz", + "integrity": "sha512-v5pJOkCxtxcSX1Cflskz9w+7kbP3PDsE6ce3zvmdCghCRAdM0SoJMffGlg/08VXwqW+GMJTZu+i+ojXMXhZTJw==", "dependencies": { "@aws-crypto/sha256-browser": "2.0.0", "@aws-crypto/sha256-js": "2.0.0", @@ -270,15 +270,15 @@ "@aws-sdk/node-config-provider": "3.127.0", "@aws-sdk/node-http-handler": "3.127.0", "@aws-sdk/protocol-http": "3.127.0", - "@aws-sdk/smithy-client": "3.127.0", + "@aws-sdk/smithy-client": "3.142.0", "@aws-sdk/types": "3.127.0", "@aws-sdk/url-parser": "3.127.0", "@aws-sdk/util-base64-browser": "3.109.0", "@aws-sdk/util-base64-node": "3.55.0", - "@aws-sdk/util-body-length-browser": "3.55.0", + "@aws-sdk/util-body-length-browser": "3.154.0", "@aws-sdk/util-body-length-node": "3.55.0", - "@aws-sdk/util-defaults-mode-browser": "3.127.0", - "@aws-sdk/util-defaults-mode-node": "3.130.0", + "@aws-sdk/util-defaults-mode-browser": "3.142.0", + "@aws-sdk/util-defaults-mode-node": "3.142.0", "@aws-sdk/util-user-agent-browser": "3.127.0", "@aws-sdk/util-user-agent-node": "3.127.0", "@aws-sdk/util-utf8-browser": "3.109.0", @@ -295,14 +295,14 @@ "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" }, "node_modules/@aws-sdk/client-sts": { - "version": "3.131.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.131.0.tgz", - "integrity": "sha512-D9GAnF8n3VwFhaE+jxXH035ZZ24WxpY36DUxszCRwXbA7qFazY1BTs1WoKFr8tDH4/iUUqCXd8NuA1l4RiwnqQ==", + "version": "3.154.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.154.0.tgz", + "integrity": "sha512-YFyyJ6GJbd0DpLqByqG7DXf/b6bEfzWer+MqUEdkomEy5smCPMfqlZOXrm1cCcqZbJiOb5ASJslQr6TLllLNIg==", "dependencies": { "@aws-crypto/sha256-browser": "2.0.0", "@aws-crypto/sha256-js": "2.0.0", "@aws-sdk/config-resolver": "3.130.0", - "@aws-sdk/credential-provider-node": "3.131.0", + "@aws-sdk/credential-provider-node": "3.154.0", "@aws-sdk/fetch-http-handler": "3.131.0", "@aws-sdk/hash-node": "3.127.0", "@aws-sdk/invalid-dependency": "3.127.0", @@ -319,15 +319,15 @@ "@aws-sdk/node-config-provider": "3.127.0", "@aws-sdk/node-http-handler": "3.127.0", "@aws-sdk/protocol-http": "3.127.0", - "@aws-sdk/smithy-client": "3.127.0", + "@aws-sdk/smithy-client": "3.142.0", "@aws-sdk/types": "3.127.0", "@aws-sdk/url-parser": "3.127.0", "@aws-sdk/util-base64-browser": "3.109.0", "@aws-sdk/util-base64-node": "3.55.0", - "@aws-sdk/util-body-length-browser": "3.55.0", + "@aws-sdk/util-body-length-browser": "3.154.0", "@aws-sdk/util-body-length-node": "3.55.0", - "@aws-sdk/util-defaults-mode-browser": "3.127.0", - "@aws-sdk/util-defaults-mode-node": "3.130.0", + "@aws-sdk/util-defaults-mode-browser": "3.142.0", + "@aws-sdk/util-defaults-mode-node": "3.142.0", "@aws-sdk/util-user-agent-browser": "3.127.0", "@aws-sdk/util-user-agent-node": "3.127.0", "@aws-sdk/util-utf8-browser": "3.109.0", @@ -404,13 +404,13 @@ "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" }, "node_modules/@aws-sdk/credential-provider-ini": { - "version": "3.131.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.131.0.tgz", - "integrity": "sha512-0hA2ZwRUDmG5Wp/1t5BLvju2kZft1T3b3KC068ZY3t1+t/O46R6R9vINKEodohKTbfmGddu+aGY58Ai+N7O5Xw==", + "version": "3.154.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.154.0.tgz", + "integrity": "sha512-5p8vueRuAMo3cMBAHQCgAu6Kr+K6R64Bm1yccQu72HEy8zoyQsCKMV0tQS7dYbObfOGpIXZbHyESyTon0khI0g==", "dependencies": { "@aws-sdk/credential-provider-env": "3.127.0", "@aws-sdk/credential-provider-imds": "3.127.0", - "@aws-sdk/credential-provider-sso": "3.131.0", + "@aws-sdk/credential-provider-sso": "3.154.0", "@aws-sdk/credential-provider-web-identity": "3.127.0", "@aws-sdk/property-provider": "3.127.0", "@aws-sdk/shared-ini-file-loader": "3.127.0", @@ -427,15 +427,15 @@ "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" }, "node_modules/@aws-sdk/credential-provider-node": { - "version": "3.131.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.131.0.tgz", - "integrity": "sha512-nVQ6P91nd7i/G+iEnKWVwRRsQZIdY0qfza2+v70fOphjv0vzgDN7Xbn1GiYQVbxBiuxMSjQqg1r/p9PdRmt6QA==", + "version": "3.154.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.154.0.tgz", + "integrity": "sha512-pNxKtf/ye2574+QT2aKykSzKo3RnwCtWB7Tduo/8YlmQZL+/vX53BLcGj+fLOE1h7RbY5psF02dzbanvb4CVGg==", "dependencies": { "@aws-sdk/credential-provider-env": "3.127.0", "@aws-sdk/credential-provider-imds": "3.127.0", - "@aws-sdk/credential-provider-ini": "3.131.0", + "@aws-sdk/credential-provider-ini": "3.154.0", "@aws-sdk/credential-provider-process": "3.127.0", - "@aws-sdk/credential-provider-sso": "3.131.0", + "@aws-sdk/credential-provider-sso": "3.154.0", "@aws-sdk/credential-provider-web-identity": "3.127.0", "@aws-sdk/property-provider": "3.127.0", "@aws-sdk/shared-ini-file-loader": "3.127.0", @@ -471,11 +471,11 @@ "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" }, "node_modules/@aws-sdk/credential-provider-sso": { - "version": "3.131.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.131.0.tgz", - "integrity": "sha512-3LVan87e6NqnwUrpmjM5dbx8LbZyGG7Gdzf68YL0tZFptCFh1mR/kTJCToGX/hm7Jf3SRU3wtUWJ6G72yP72Sw==", + "version": "3.154.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.154.0.tgz", + "integrity": "sha512-w3EZo1IKLyE7rhurq56e8IZuMxr0bc3Qvkq+AJnDwTR4sm5TPp9RNJwo+/A0i7GOdhNufcTlaciZT9Izi3g4+A==", "dependencies": { - "@aws-sdk/client-sso": "3.131.0", + "@aws-sdk/client-sso": "3.154.0", "@aws-sdk/property-provider": "3.127.0", "@aws-sdk/shared-ini-file-loader": "3.127.0", "@aws-sdk/types": "3.127.0", @@ -1188,9 +1188,9 @@ "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" }, "node_modules/@aws-sdk/smithy-client": { - "version": "3.127.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/smithy-client/-/smithy-client-3.127.0.tgz", - "integrity": "sha512-sfcAJ+7a41CJMtsv6HRIjA91155Yk013RvMUdG2EMSo3cpLq/QmTJ1EGw4ByDZs5HLpXAaRoLI+bA2ovriGQnQ==", + "version": "3.142.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/smithy-client/-/smithy-client-3.142.0.tgz", + "integrity": "sha512-G38YWTfSFZb5cOH6IwLct530Uy8pnmJvJFeC1pd1nkKD4PRZb+bI2w4xXSX+znYdLA71RYK620OtVKJlB44PtA==", "dependencies": { "@aws-sdk/middleware-stack": "3.127.0", "@aws-sdk/types": "3.127.0", @@ -1275,9 +1275,9 @@ "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" }, "node_modules/@aws-sdk/util-body-length-browser": { - "version": "3.55.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-body-length-browser/-/util-body-length-browser-3.55.0.tgz", - "integrity": "sha512-Ei2OCzXQw5N6ZkTMZbamUzc1z+z1R1Ja5tMEagz5BxuX4vWdBObT+uGlSzL8yvTbjoPjnxWA2aXyEqaUP3JS8Q==", + "version": "3.154.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-body-length-browser/-/util-body-length-browser-3.154.0.tgz", + "integrity": "sha512-TUuy7paVkBRQrB/XFCsL8iTW6g/ma0S3N8dYOiIMJdeTqTFryeyOGkBpYBgYFQL6zRMZpyu0jOM7GYEffGFOXw==", "dependencies": { "tslib": "^2.3.1" } @@ -1337,9 +1337,9 @@ "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" }, "node_modules/@aws-sdk/util-defaults-mode-browser": { - "version": "3.127.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-defaults-mode-browser/-/util-defaults-mode-browser-3.127.0.tgz", - "integrity": "sha512-e/vBm+EYSJ0R79591EPiCPE3aR5RKk5CjOkQjNxZIX8UPnIlo7xohTcebfR/iugSTxNrpfrFv+o4H5GjzAuhLA==", + "version": "3.142.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-defaults-mode-browser/-/util-defaults-mode-browser-3.142.0.tgz", + "integrity": "sha512-vVB/CrodMmIfv4v54MyBlKO0sQSI/+Mvs4g5gMyVjmT4a+1gnktJQ9R6ZHQ2/ErGewcra6eH9MU5T0r1kYe0+w==", "dependencies": { "@aws-sdk/property-provider": "3.127.0", "@aws-sdk/types": "3.127.0", @@ -1356,9 +1356,9 @@ "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" }, "node_modules/@aws-sdk/util-defaults-mode-node": { - "version": "3.130.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-defaults-mode-node/-/util-defaults-mode-node-3.130.0.tgz", - "integrity": "sha512-0BWx7C6GhHBrjPUuSgMnRA4InxYisX6MIGs5yIHk2OArYkQLJMdeORYXXz1y40ahMihmtjD/Ap5xQGBm2vyffA==", + "version": "3.142.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-defaults-mode-node/-/util-defaults-mode-node-3.142.0.tgz", + "integrity": "sha512-13d5RZLO13EDwll3COUq3D4KVsqM63kdf+YjG5mzXR1eXo6GVjghfQfiy0MYM6YbAjTfJxZQkc0nFgWLU8jdyg==", "dependencies": { "@aws-sdk/config-resolver": "3.130.0", "@aws-sdk/credential-provider-imds": "3.127.0", @@ -1567,9 +1567,9 @@ "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" }, "node_modules/@aws-sdk/xml-builder": { - "version": "3.109.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.109.0.tgz", - "integrity": "sha512-+aAXynnrqya1Eukz4Gxch4xIXCZolIMWGD4Ll/Q5yXT5uAjGh2HQWd9J0LWE+gYChpWetZbAVYZ3cEJ6F+SpZA==", + "version": "3.142.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.142.0.tgz", + "integrity": "sha512-e8rFjm5y9ngFc/cPwWMNn/CmMMrLx98CajWew9q7OzP6OOXQJ0H6TaRps2uQPM5XUv3/Ab5YQCV3NiaLJLqqNg==", "dependencies": { "tslib": "^2.3.1" }, @@ -5585,16 +5585,16 @@ } }, "@aws-sdk/client-s3": { - "version": "3.131.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-s3/-/client-s3-3.131.0.tgz", - "integrity": "sha512-ue6uzk04pRJCJIaU1xKAW7Fx4TJx5n5Dwtycm0H94msj5HpJOfDKPoO9+kbywZywWP7n+eNnzNl/6lMjCSfO4g==", + "version": "3.154.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-s3/-/client-s3-3.154.0.tgz", + "integrity": "sha512-TUkUZUmDuQlh7N6455LNqjuky/iIhjvnISJzl+wMKyQUMX61FPDZMYDG7HXDEQGV7uaAvjaabKfyHVNdoi/SmA==", "requires": { "@aws-crypto/sha1-browser": "2.0.0", "@aws-crypto/sha256-browser": "2.0.0", "@aws-crypto/sha256-js": "2.0.0", - "@aws-sdk/client-sts": "3.131.0", + "@aws-sdk/client-sts": "3.154.0", "@aws-sdk/config-resolver": "3.130.0", - "@aws-sdk/credential-provider-node": "3.131.0", + "@aws-sdk/credential-provider-node": "3.154.0", "@aws-sdk/eventstream-serde-browser": "3.127.0", "@aws-sdk/eventstream-serde-config-resolver": "3.127.0", "@aws-sdk/eventstream-serde-node": "3.127.0", @@ -5623,15 +5623,15 @@ "@aws-sdk/node-http-handler": "3.127.0", "@aws-sdk/protocol-http": "3.127.0", "@aws-sdk/signature-v4-multi-region": "3.130.0", - "@aws-sdk/smithy-client": "3.127.0", + "@aws-sdk/smithy-client": "3.142.0", "@aws-sdk/types": "3.127.0", "@aws-sdk/url-parser": "3.127.0", "@aws-sdk/util-base64-browser": "3.109.0", "@aws-sdk/util-base64-node": "3.55.0", - "@aws-sdk/util-body-length-browser": "3.55.0", + "@aws-sdk/util-body-length-browser": "3.154.0", "@aws-sdk/util-body-length-node": "3.55.0", - "@aws-sdk/util-defaults-mode-browser": "3.127.0", - "@aws-sdk/util-defaults-mode-node": "3.130.0", + "@aws-sdk/util-defaults-mode-browser": "3.142.0", + "@aws-sdk/util-defaults-mode-node": "3.142.0", "@aws-sdk/util-stream-browser": "3.131.0", "@aws-sdk/util-stream-node": "3.129.0", "@aws-sdk/util-user-agent-browser": "3.127.0", @@ -5639,7 +5639,7 @@ "@aws-sdk/util-utf8-browser": "3.109.0", "@aws-sdk/util-utf8-node": "3.109.0", "@aws-sdk/util-waiter": "3.127.0", - "@aws-sdk/xml-builder": "3.109.0", + "@aws-sdk/xml-builder": "3.142.0", "entities": "2.2.0", "fast-xml-parser": "3.19.0", "tslib": "^2.3.1" @@ -5653,9 +5653,9 @@ } }, "@aws-sdk/client-sso": { - "version": "3.131.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.131.0.tgz", - "integrity": "sha512-6fbjqLdVZF7mvGpHjWX5YsqBE/99MilNtGUFlwuf4/KnmYy49V16A6Dltnd43Hu6HVGxJ8caH9nCkIdNp3YZcQ==", + "version": "3.154.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.154.0.tgz", + "integrity": "sha512-v5pJOkCxtxcSX1Cflskz9w+7kbP3PDsE6ce3zvmdCghCRAdM0SoJMffGlg/08VXwqW+GMJTZu+i+ojXMXhZTJw==", "requires": { "@aws-crypto/sha256-browser": "2.0.0", "@aws-crypto/sha256-js": "2.0.0", @@ -5674,15 +5674,15 @@ "@aws-sdk/node-config-provider": "3.127.0", "@aws-sdk/node-http-handler": "3.127.0", "@aws-sdk/protocol-http": "3.127.0", - "@aws-sdk/smithy-client": "3.127.0", + "@aws-sdk/smithy-client": "3.142.0", "@aws-sdk/types": "3.127.0", "@aws-sdk/url-parser": "3.127.0", "@aws-sdk/util-base64-browser": "3.109.0", "@aws-sdk/util-base64-node": "3.55.0", - "@aws-sdk/util-body-length-browser": "3.55.0", + "@aws-sdk/util-body-length-browser": "3.154.0", "@aws-sdk/util-body-length-node": "3.55.0", - "@aws-sdk/util-defaults-mode-browser": "3.127.0", - "@aws-sdk/util-defaults-mode-node": "3.130.0", + "@aws-sdk/util-defaults-mode-browser": "3.142.0", + "@aws-sdk/util-defaults-mode-node": "3.142.0", "@aws-sdk/util-user-agent-browser": "3.127.0", "@aws-sdk/util-user-agent-node": "3.127.0", "@aws-sdk/util-utf8-browser": "3.109.0", @@ -5698,14 +5698,14 @@ } }, "@aws-sdk/client-sts": { - "version": "3.131.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.131.0.tgz", - "integrity": "sha512-D9GAnF8n3VwFhaE+jxXH035ZZ24WxpY36DUxszCRwXbA7qFazY1BTs1WoKFr8tDH4/iUUqCXd8NuA1l4RiwnqQ==", + "version": "3.154.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.154.0.tgz", + "integrity": "sha512-YFyyJ6GJbd0DpLqByqG7DXf/b6bEfzWer+MqUEdkomEy5smCPMfqlZOXrm1cCcqZbJiOb5ASJslQr6TLllLNIg==", "requires": { "@aws-crypto/sha256-browser": "2.0.0", "@aws-crypto/sha256-js": "2.0.0", "@aws-sdk/config-resolver": "3.130.0", - "@aws-sdk/credential-provider-node": "3.131.0", + "@aws-sdk/credential-provider-node": "3.154.0", "@aws-sdk/fetch-http-handler": "3.131.0", "@aws-sdk/hash-node": "3.127.0", "@aws-sdk/invalid-dependency": "3.127.0", @@ -5722,15 +5722,15 @@ "@aws-sdk/node-config-provider": "3.127.0", "@aws-sdk/node-http-handler": "3.127.0", "@aws-sdk/protocol-http": "3.127.0", - "@aws-sdk/smithy-client": "3.127.0", + "@aws-sdk/smithy-client": "3.142.0", "@aws-sdk/types": "3.127.0", "@aws-sdk/url-parser": "3.127.0", "@aws-sdk/util-base64-browser": "3.109.0", "@aws-sdk/util-base64-node": "3.55.0", - "@aws-sdk/util-body-length-browser": "3.55.0", + "@aws-sdk/util-body-length-browser": "3.154.0", "@aws-sdk/util-body-length-node": "3.55.0", - "@aws-sdk/util-defaults-mode-browser": "3.127.0", - "@aws-sdk/util-defaults-mode-node": "3.130.0", + "@aws-sdk/util-defaults-mode-browser": "3.142.0", + "@aws-sdk/util-defaults-mode-node": "3.142.0", "@aws-sdk/util-user-agent-browser": "3.127.0", "@aws-sdk/util-user-agent-node": "3.127.0", "@aws-sdk/util-utf8-browser": "3.109.0", @@ -5803,13 +5803,13 @@ } }, "@aws-sdk/credential-provider-ini": { - "version": "3.131.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.131.0.tgz", - "integrity": "sha512-0hA2ZwRUDmG5Wp/1t5BLvju2kZft1T3b3KC068ZY3t1+t/O46R6R9vINKEodohKTbfmGddu+aGY58Ai+N7O5Xw==", + "version": "3.154.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.154.0.tgz", + "integrity": "sha512-5p8vueRuAMo3cMBAHQCgAu6Kr+K6R64Bm1yccQu72HEy8zoyQsCKMV0tQS7dYbObfOGpIXZbHyESyTon0khI0g==", "requires": { "@aws-sdk/credential-provider-env": "3.127.0", "@aws-sdk/credential-provider-imds": "3.127.0", - "@aws-sdk/credential-provider-sso": "3.131.0", + "@aws-sdk/credential-provider-sso": "3.154.0", "@aws-sdk/credential-provider-web-identity": "3.127.0", "@aws-sdk/property-provider": "3.127.0", "@aws-sdk/shared-ini-file-loader": "3.127.0", @@ -5825,15 +5825,15 @@ } }, "@aws-sdk/credential-provider-node": { - "version": "3.131.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.131.0.tgz", - "integrity": "sha512-nVQ6P91nd7i/G+iEnKWVwRRsQZIdY0qfza2+v70fOphjv0vzgDN7Xbn1GiYQVbxBiuxMSjQqg1r/p9PdRmt6QA==", + "version": "3.154.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.154.0.tgz", + "integrity": "sha512-pNxKtf/ye2574+QT2aKykSzKo3RnwCtWB7Tduo/8YlmQZL+/vX53BLcGj+fLOE1h7RbY5psF02dzbanvb4CVGg==", "requires": { "@aws-sdk/credential-provider-env": "3.127.0", "@aws-sdk/credential-provider-imds": "3.127.0", - "@aws-sdk/credential-provider-ini": "3.131.0", + "@aws-sdk/credential-provider-ini": "3.154.0", "@aws-sdk/credential-provider-process": "3.127.0", - "@aws-sdk/credential-provider-sso": "3.131.0", + "@aws-sdk/credential-provider-sso": "3.154.0", "@aws-sdk/credential-provider-web-identity": "3.127.0", "@aws-sdk/property-provider": "3.127.0", "@aws-sdk/shared-ini-file-loader": "3.127.0", @@ -5867,11 +5867,11 @@ } }, "@aws-sdk/credential-provider-sso": { - "version": "3.131.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.131.0.tgz", - "integrity": "sha512-3LVan87e6NqnwUrpmjM5dbx8LbZyGG7Gdzf68YL0tZFptCFh1mR/kTJCToGX/hm7Jf3SRU3wtUWJ6G72yP72Sw==", + "version": "3.154.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.154.0.tgz", + "integrity": "sha512-w3EZo1IKLyE7rhurq56e8IZuMxr0bc3Qvkq+AJnDwTR4sm5TPp9RNJwo+/A0i7GOdhNufcTlaciZT9Izi3g4+A==", "requires": { - "@aws-sdk/client-sso": "3.131.0", + "@aws-sdk/client-sso": "3.154.0", "@aws-sdk/property-provider": "3.127.0", "@aws-sdk/shared-ini-file-loader": "3.127.0", "@aws-sdk/types": "3.127.0", @@ -6549,9 +6549,9 @@ } }, "@aws-sdk/smithy-client": { - "version": "3.127.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/smithy-client/-/smithy-client-3.127.0.tgz", - "integrity": "sha512-sfcAJ+7a41CJMtsv6HRIjA91155Yk013RvMUdG2EMSo3cpLq/QmTJ1EGw4ByDZs5HLpXAaRoLI+bA2ovriGQnQ==", + "version": "3.142.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/smithy-client/-/smithy-client-3.142.0.tgz", + "integrity": "sha512-G38YWTfSFZb5cOH6IwLct530Uy8pnmJvJFeC1pd1nkKD4PRZb+bI2w4xXSX+znYdLA71RYK620OtVKJlB44PtA==", "requires": { "@aws-sdk/middleware-stack": "3.127.0", "@aws-sdk/types": "3.127.0", @@ -6634,9 +6634,9 @@ } }, "@aws-sdk/util-body-length-browser": { - "version": "3.55.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-body-length-browser/-/util-body-length-browser-3.55.0.tgz", - "integrity": "sha512-Ei2OCzXQw5N6ZkTMZbamUzc1z+z1R1Ja5tMEagz5BxuX4vWdBObT+uGlSzL8yvTbjoPjnxWA2aXyEqaUP3JS8Q==", + "version": "3.154.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-body-length-browser/-/util-body-length-browser-3.154.0.tgz", + "integrity": "sha512-TUuy7paVkBRQrB/XFCsL8iTW6g/ma0S3N8dYOiIMJdeTqTFryeyOGkBpYBgYFQL6zRMZpyu0jOM7GYEffGFOXw==", "requires": { "tslib": "^2.3.1" }, @@ -6695,9 +6695,9 @@ } }, "@aws-sdk/util-defaults-mode-browser": { - "version": "3.127.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-defaults-mode-browser/-/util-defaults-mode-browser-3.127.0.tgz", - "integrity": "sha512-e/vBm+EYSJ0R79591EPiCPE3aR5RKk5CjOkQjNxZIX8UPnIlo7xohTcebfR/iugSTxNrpfrFv+o4H5GjzAuhLA==", + "version": "3.142.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-defaults-mode-browser/-/util-defaults-mode-browser-3.142.0.tgz", + "integrity": "sha512-vVB/CrodMmIfv4v54MyBlKO0sQSI/+Mvs4g5gMyVjmT4a+1gnktJQ9R6ZHQ2/ErGewcra6eH9MU5T0r1kYe0+w==", "requires": { "@aws-sdk/property-provider": "3.127.0", "@aws-sdk/types": "3.127.0", @@ -6713,9 +6713,9 @@ } }, "@aws-sdk/util-defaults-mode-node": { - "version": "3.130.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-defaults-mode-node/-/util-defaults-mode-node-3.130.0.tgz", - "integrity": "sha512-0BWx7C6GhHBrjPUuSgMnRA4InxYisX6MIGs5yIHk2OArYkQLJMdeORYXXz1y40ahMihmtjD/Ap5xQGBm2vyffA==", + "version": "3.142.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-defaults-mode-node/-/util-defaults-mode-node-3.142.0.tgz", + "integrity": "sha512-13d5RZLO13EDwll3COUq3D4KVsqM63kdf+YjG5mzXR1eXo6GVjghfQfiy0MYM6YbAjTfJxZQkc0nFgWLU8jdyg==", "requires": { "@aws-sdk/config-resolver": "3.130.0", "@aws-sdk/credential-provider-imds": "3.127.0", @@ -6913,9 +6913,9 @@ } }, "@aws-sdk/xml-builder": { - "version": "3.109.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.109.0.tgz", - "integrity": "sha512-+aAXynnrqya1Eukz4Gxch4xIXCZolIMWGD4Ll/Q5yXT5uAjGh2HQWd9J0LWE+gYChpWetZbAVYZ3cEJ6F+SpZA==", + "version": "3.142.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.142.0.tgz", + "integrity": "sha512-e8rFjm5y9ngFc/cPwWMNn/CmMMrLx98CajWew9q7OzP6OOXQJ0H6TaRps2uQPM5XUv3/Ab5YQCV3NiaLJLqqNg==", "requires": { "tslib": "^2.3.1" }, diff --git a/package.json b/package.json index 4d62f7ddf39686d58649ad5940a69b42ae3cd786..cc926294363c557cd8ac4c8fc333ed69919e7b4a 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ "generate-client": "openapi --input http://localhost:9999/api/openapi.json --output src/client --client axios" }, "dependencies": { - "@aws-sdk/client-s3": "^3.131.0", + "@aws-sdk/client-s3": "^3.154.0", "@popperjs/core": "^2.11.5", "bootstrap": "^5.2.0", "bootstrap-icons": "^1.9.1", diff --git a/src/components/BootstrapModal.vue b/src/components/BootstrapModal.vue index 09aece78d3e3388c5d47e6a3cc40887c8b008542..b03400b3ddb79ecb114aebdd355297346641ccb2 100644 --- a/src/components/BootstrapModal.vue +++ b/src/components/BootstrapModal.vue @@ -16,7 +16,7 @@ defineProps<{ :data-bs-backdrop="staticBackdrop ? 'static' : null" > <div - class="modal-dialog modal-dialog-centered text-dark" + class="modal-dialog modal-dialog-centered modal-dialog-scrollable text-dark" style="min-width: 25%" > <div class="modal-content"> diff --git a/src/components/BucketListItem.vue b/src/components/BucketListItem.vue index a8ba47b02a04ba7b708997a563b9b0aa82757638..05cb78fadc59f74bbfcba88626d0c27a0c297ec8 100644 --- a/src/components/BucketListItem.vue +++ b/src/components/BucketListItem.vue @@ -1,6 +1,7 @@ <script setup lang="ts"> -import type { BucketOut } from "@/client"; +import type { BucketOut, BucketPermission } from "@/client"; import BootstrapIcon from "@/components/BootstrapIcon.vue"; +import PermissionModal from "@/components/PermissionModal.vue"; import dayjs from "dayjs"; import fileSize from "filesize"; import { onMounted } from "vue"; @@ -10,22 +11,37 @@ const props = defineProps<{ active: boolean; bucket: BucketOut; loading: boolean; + permission: BucketPermission | undefined; }>(); -const tooltipID = Math.random().toString(16).substr(2, 8); +const randomIDSuffix = Math.random().toString(16).substr(2, 8); const emit = defineEmits<{ - (e: "delete-bucket", bucket_name: string): void; + (e: "delete-bucket", bucketName: string): void; + (e: "permission-deleted", bucketName: string): void; }>(); onMounted(() => { if (!props.loading) { - new Tooltip("#tooltip-" + tooltipID); + new Tooltip("#tooltip-" + randomIDSuffix); } }); </script> <template> + <permission-modal + v-if="props.permission != null && props.active" + :modalID="'view-permission-modal' + randomIDSuffix" + modal-label="view-permission-modal-label" + :bucket-name="props.bucket.name" + :sub-folders="{ subFolders: {}, files: [] }" + :edit-user-permission="props.permission" + :readonly="true" + :editable="false" + :deletable="true" + :back-modal-id="undefined" + @permission-deleted="(perm) => emit('permission-deleted', perm.bucket_name)" + /> <div class="mt-2 mb-2"> <div v-if="loading" @@ -49,25 +65,40 @@ onMounted(() => { > {{ bucket.name }} <bootstrap-icon - v-if="props.active" + v-if="props.active && props.permission == null" icon="trash-fill" + class="delete-icon" :width="16" :height="16" - fill="white" + fill="currentColor" @click="emit('delete-bucket', bucket.name)" /> </router-link> <div :hidden="!props.active" - class="ps-2 rounded-bottom bg-light text-bg-light" + class="ps-2 pe-2 rounded-bottom bg-light text-bg-light border border-3 border-top-0 border-primary" > - <table class="table table-sm table-borderless"> + <div v-if="props.permission != null" class="ms-1 pt-1 text-info"> + Foreign Bucket + </div> + <table class="table table-sm table-borderless mb-0"> <tbody> + <tr v-if="props.permission != null"> + <th scope="row" class="fw-bold">Permission</th> + <td> + <a + href="#" + data-bs-toggle="modal" + :data-bs-target="'#view-permission-modal' + randomIDSuffix" + >View</a + > + </td> + </tr> <tr> <th scope="row" class="fw-bold">Created:</th> <td> <span - :id="'tooltip-' + tooltipID" + :id="'tooltip-' + randomIDSuffix" data-bs-toggle="tooltip" :data-bs-title=" dayjs(bucket.created_at).format('DD.MM.YYYY HH:mm:ss') @@ -92,4 +123,11 @@ onMounted(() => { </div> </template> -<style scoped></style> +<style scoped> +.delete-icon { + color: white; +} +.delete-icon:hover { + color: var(--bs-danger); +} +</style> diff --git a/src/components/BucketView.vue b/src/components/BucketView.vue index 4e09fa38bc6a2efc5b4b5cb7add31c81df1a0511..17f601165c37c95ebcfcd9114be07d2c9deb5df7 100644 --- a/src/components/BucketView.vue +++ b/src/components/BucketView.vue @@ -1,12 +1,13 @@ <script setup lang="ts"> import { onMounted, reactive, watch, computed } from "vue"; import type { ComputedRef } from "vue"; -import type { S3ObjectMetaInformation } from "@/client"; +import type { S3ObjectMetaInformation, BucketPermission } from "@/client"; import { ObjectService } from "@/client"; import BootstrapIcon from "@/components/BootstrapIcon.vue"; import fileSize from "filesize"; import dayjs from "dayjs"; import { Tooltip } from "bootstrap"; +import PermissionListModal from "@/components/PermissionListModal.vue"; import PermissionModal from "@/components/PermissionModal.vue"; // Constants @@ -15,6 +16,7 @@ import PermissionModal from "@/components/PermissionModal.vue"; const props = defineProps<{ bucketName: string; subFolders: string[] | string; + permission: BucketPermission | undefined; }>(); // Typescript types @@ -44,11 +46,13 @@ const objectState = reactive({ loading: true, bucketNotFoundError: false, bucketPermissionError: false, + createdPermission: undefined, } as { objects: S3ObjectMetaInformation[]; loading: boolean; bucketNotFoundError: boolean; bucketPermissionError: boolean; + createdPermission: undefined | BucketPermission; }); // Watcher @@ -186,6 +190,11 @@ const errorLoadingObjects: ComputedRef<boolean> = computed( // ----------------------------------------------------------------------------- onMounted(() => { updateObjects(props.bucketName); + document + .querySelectorAll(".tooltip-container") + .forEach( + (tooltipTriggerEl) => new Tooltip(tooltipTriggerEl, { trigger: "hover" }) + ); }); // Functions @@ -329,17 +338,32 @@ watch( </div> </div> <!-- Upload object button --> - <div class="col-auto"> + <div id="BucketViewButtons" class="col-auto"> <button type="button" - class="btn btn-secondary me-2" + class="btn btn-secondary me-2 tooltip-container" :disabled="errorLoadingObjects" + data-bs-toggle="tooltip" + data-bs-title="Upload Object" > <bootstrap-icon icon="upload" :width="16" :height="16" fill="white" /> <span class="visually-hidden">Upload Object</span> </button> + <!-- Add folder button --> + <button + type="button" + class="btn btn-secondary m-2 tooltip-container" + :disabled="errorLoadingObjects" + data-bs-toggle="tooltip" + data-bs-title="Create Folder" + > + <bootstrap-icon icon="plus-lg" :width="16" :height="16" fill="white" /> + Folder + <span class="visually-hidden">Add Folder</span> + </button> <!-- Add bucket permission button --> <button + :hidden="props.permission != null" type="button" class="btn btn-secondary m-2" :disabled="errorLoadingObjects" @@ -360,18 +384,38 @@ watch( :bucket-name="props.bucketName" :sub-folders="folderStructure" :edit-user-permission="undefined" + :editable="false" :readonly="false" + :deletable="false" + :back-modal-id="undefined" + @permission-created=" + (newPermission) => (objectState.createdPermission = newPermission) + " /> - <!-- Add folder button --> <button + :hidden="props.permission != null" type="button" - class="btn btn-secondary m-2" + class="btn btn-secondary m-2 tooltip-container" :disabled="errorLoadingObjects" + data-bs-title="List Bucket Permission" + data-bs-toggle="modal" + data-bs-target="#permission-list-modal" > - <bootstrap-icon icon="plus-lg" :width="16" :height="16" fill="white" /> - Folder - <span class="visually-hidden">Add Folder</span> + <bootstrap-icon + icon="person-lines-fill" + :width="16" + :height="16" + fill="white" + /> + <span class="visually-hidden">View Bucket Permissions</span> </button> + <permission-list-modal + v-if="props.permission == null" + :bucket-name="props.bucketName" + :sub-folders="folderStructure" + modalID="permission-list-modal" + :add-permission="objectState.createdPermission" + /> </div> </div> <!-- Body --> diff --git a/src/components/NavbarTop.vue b/src/components/NavbarTop.vue index 429ffab8c058cb954747f2d66c745e923bb9dcd6..3cfd1ec3d574cbb5f289903c1e5282fafeffb3a1 100644 --- a/src/components/NavbarTop.vue +++ b/src/components/NavbarTop.vue @@ -51,7 +51,7 @@ watch( onMounted(() => { checkApiHealth(); - timer = setInterval(checkApiHealth, 10000); + timer = setInterval(checkApiHealth, 20000); }); onBeforeUnmount(() => { clearInterval(timer); diff --git a/src/components/PermissionListModal.vue b/src/components/PermissionListModal.vue new file mode 100644 index 0000000000000000000000000000000000000000..e6eb5aa88b909143936c2c0bab845906cefb905a --- /dev/null +++ b/src/components/PermissionListModal.vue @@ -0,0 +1,165 @@ +<script setup lang="ts"> +import type { BucketPermission, S3ObjectMetaInformation } from "@/client"; +import { reactive } from "vue"; +import { BucketPermissionsService } from "@/client"; +import { onBeforeMount, watch } from "vue"; +import BootstrapModal from "@/components/BootstrapModal.vue"; +import PermissionModal from "@/components/PermissionModal.vue"; +// Types +// ----------------------------------------------------------------------------- +interface S3ObjectWithFolder extends S3ObjectMetaInformation { + folder: string[]; + pseudoFileName: string; +} + +type FolderTree = { + subFolders: Record<string, FolderTree>; + files: S3ObjectWithFolder[]; +}; + +// Props +// ----------------------------------------------------------------------------- +const props = defineProps<{ + bucketName: string; + subFolders: FolderTree; + modalID: string; + addPermission: undefined | BucketPermission; +}>(); + +// Reactive State +// ----------------------------------------------------------------------------- +const state = reactive({ + permissions: [], + loading: true, + currentPermission: { + bucket_name: "bucketname", + uid: "uid", + permission: "READ", + }, +} as { + permissions: BucketPermission[]; + loading: boolean; + currentPermission: BucketPermission; +}); + +const randomIDSuffix = Math.random().toString(16).substr(2, 8); + +// Watchers +// ----------------------------------------------------------------------------- +watch( + () => props.bucketName, + (newBucketName) => { + updateBucketPermissions(newBucketName); + } +); + +watch( + () => props.addPermission, + (newBucketPermission) => { + if (newBucketPermission !== undefined) { + state.permissions.push(newBucketPermission); + } + } +); + +// Function +// ----------------------------------------------------------------------------- +function updateBucketPermissions(bucketName: string) { + state.loading = true; + BucketPermissionsService.bucketPermissionsListPermissionsPerBucket(bucketName) + .then((permissions) => { + state.permissions = permissions; + }) + .catch((err) => { + console.error(err); + }) + .finally(() => { + state.loading = false; + }); +} + +function permissionDeleted(bucketPermission: BucketPermission) { + state.permissions = state.permissions.filter( + (perm) => perm.uid != bucketPermission.uid + ); +} + +function permissionCreated(bucketPermission: BucketPermission) { + state.permissions.push(bucketPermission); +} + +function permissionEdited(bucketPermission: BucketPermission) { + const index = state.permissions.findIndex( + (perm) => perm.uid == bucketPermission.uid + ); + state.permissions[index] = bucketPermission; +} + +// Lifecycle Hooks +// ----------------------------------------------------------------------------- +onBeforeMount(() => { + updateBucketPermissions(props.bucketName); +}); +</script> + +<template> + <permission-modal + :deletable="true" + :editable="true" + :readonly="true" + :edit-user-permission="state.currentPermission" + :bucket-name="state.currentPermission.bucket_name" + :sub-folders="props.subFolders" + :back-modal-id="props.modalID" + :modalID="'permission-list-edit-modal' + randomIDSuffix" + modal-label="permission-list-edit" + @permission-deleted="permissionDeleted" + @permission-created="permissionCreated" + @permission-edited="permissionEdited" + /> + <bootstrap-modal + :modalID="props.modalID" + :static-backdrop="true" + modal-label="permission-list" + > + <template v-slot:header> Bucket Permissions </template> + <template v-slot:body> + <ul v-if="state.loading" class="list-group"> + <li + class="list-group-item list-group-item-action placeholder-wave" + v-for="n in 5" + :key="n" + > + <span class="placeholder col-2 bg-info"></span> + <span class="placeholder col-8 offset-1"></span> + </li> + </ul> + <div v-else> + <div class="list-group" v-if="state.permissions.length > 0"> + <button + type="button" + class="list-group-item list-group-item-action text-truncate" + v-for="permission in state.permissions" + :key="permission.uid" + @click="state.currentPermission = permission" + data-bs-toggle="modal" + :data-bs-target="'#permission-list-edit-modal' + randomIDSuffix" + > + <span class="text-info">{{ permission.permission }}</span> + {{ permission.uid }} + </button> + </div> + <div v-else> + <h3 class="text-center">No Bucket Permissions for this bucket yet</h3> + </div> + </div> + </template> + <template v-slot:footer> + <button type="button" class="btn btn-secondary" data-bs-dismiss="modal"> + Close + </button> + </template> + </bootstrap-modal> +</template> + +<style scoped></style> diff --git a/src/components/PermissionModal.vue b/src/components/PermissionModal.vue index 56c607cbe7aa738e6ecec50a00e739d5f5f00b59..b8809da918f0c034e74a02659947b1f576b45955 100644 --- a/src/components/PermissionModal.vue +++ b/src/components/PermissionModal.vue @@ -1,5 +1,5 @@ <script setup lang="ts"> -import { onMounted, reactive, watch, computed } from "vue"; +import { onMounted, reactive, watch, ref, computed } from "vue"; import BootstrapModal from "@/components/BootstrapModal.vue"; import { Modal } from "bootstrap"; import dayjs from "dayjs"; @@ -8,7 +8,7 @@ import type { S3ObjectMetaInformation, BucketPermissionParameters, } from "@/client"; -import type { ComputedRef } from "vue"; +import type { ComputedRef, Ref } from "vue"; import { PermissionEnum, BucketPermissionsService } from "@/client"; import { Toast } from "bootstrap"; import BootstrapIcon from "@/components/BootstrapIcon.vue"; @@ -34,12 +34,15 @@ const props = defineProps<{ subFolders: FolderTree; editUserPermission: BucketPermission | undefined; readonly: boolean; + editable: boolean; + deletable: boolean; + backModalId: string | undefined; }>(); // Variables // ----------------------------------------------------------------------------- -const toastID = Math.random().toString(16).substr(2, 8); -let createPermissionModal: Modal | null = null; +const randomIDSuffix = Math.random().toString(16).substr(2, 8); +let permissionModal: Modal | null = null; let successToast: Toast | null = null; // Reactive State @@ -63,6 +66,8 @@ const permission = reactive({ bucket_name: props.bucketName, } as BucketPermission); +const permissionDeleted: Ref<boolean> = ref(false); + // Computes Properties // ----------------------------------------------------------------------------- const editPermission: ComputedRef<boolean> = computed( @@ -92,6 +97,14 @@ watch( () => updatePermission() ); +// Events +// ----------------------------------------------------------------------------- +const emit = defineEmits<{ + (e: "permission-deleted", permission: BucketPermission): void; + (e: "permission-created", permission: BucketPermission): void; + (e: "permission-edited", permission: BucketPermission): void; +}>(); + // Functions // ----------------------------------------------------------------------------- /** @@ -106,7 +119,14 @@ function modalClosed() { } /** - * Check if a input should be visible based on its state + * Callback when the toast is hidden again. + */ +function toastHidden() { + permissionDeleted.value = false; +} + +/** + * Check if an input should be visible based on its state * @param input Input which visibility should be determined. */ function inputVisible(input: string | undefined): boolean { @@ -172,25 +192,27 @@ function formSubmit() { formState.error = false; // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const form = document.getElementById( - "permissionCreateForm" + "permissionCreateEditForm" + randomIDSuffix )! as HTMLFormElement; if (form.checkValidity()) { const tempPermission: BucketPermission = permission; if (permission.from_timestamp != null) { - tempPermission.from_timestamp = dayjs( - permission.from_timestamp - ).toISOString(); + tempPermission.from_timestamp = + permission.from_timestamp.length > 0 + ? dayjs(permission.from_timestamp).toISOString() + : undefined; } if (permission.to_timestamp != null) { - tempPermission.to_timestamp = dayjs( - permission.to_timestamp - ).toISOString(); + tempPermission.to_timestamp = + permission.to_timestamp.length > 0 + ? dayjs(permission.to_timestamp).toISOString() + : undefined; } formState.loading = true; const serverAnswerPromise = editPermission.value ? BucketPermissionsService.bucketPermissionsUpdatePermission( - permission.bucket_name, permission.uid, + permission.bucket_name, { to_timestamp: tempPermission.to_timestamp, from_timestamp: tempPermission.from_timestamp, @@ -202,13 +224,45 @@ function formSubmit() { tempPermission ); serverAnswerPromise + .then((permission) => { + if (editPermission.value) { + emit("permission-edited", permission); + } else { + emit("permission-created", permission); + } + permissionModal?.hide(); + successToast?.show(); + updatePermission(); + }) + .catch(() => { + formState.error = true; + }) + .finally(() => { + formState.loading = false; + }); + } +} + +/** + * Delete a permission for a bucket user combination + * @param bucketName Bucket to delete + * @param uid ID of grantee of the permission + */ +function deletePermission(bucketName: string, uid: string) { + if (!formState.loading) { + formState.loading = true; + BucketPermissionsService.bucketPermissionsDeletePermissionForBucket( + bucketName, + uid + ) .then(() => { - createPermissionModal?.hide(); + permissionDeleted.value = true; + permissionModal?.hide(); successToast?.show(); + emit("permission-deleted", permission); }) - .catch((err) => { + .catch(() => { formState.error = true; - console.error(err); }) .finally(() => { formState.loading = false; @@ -219,8 +273,9 @@ function formSubmit() { // Lifecycle Hooks // ----------------------------------------------------------------------------- onMounted(() => { - createPermissionModal = new Modal("#" + props.modalID); - successToast = new Toast("#" + "toast-" + toastID, { autohide: true }); + permissionModal = new Modal("#" + props.modalID); + successToast = new Toast("#" + "toast-" + randomIDSuffix, { autohide: true }); + updatePermission(); }); </script> @@ -232,13 +287,15 @@ onMounted(() => { aria-atomic="true" class="toast text-bg-success align-items-center border-0" data-bs-autohide="false" - :id="'toast-' + toastID" + :id="'toast-' + randomIDSuffix" + v-on="{ 'hidden.bs.toast': toastHidden }" > <div class="d-flex"> <div class="toast-body"> Successfully - <span v-if="editPermission">created</span> - <span v-else>edited</span> + <span v-if="permissionDeleted">deleted</span> + <span v-else-if="editPermission">edited</span> + <span v-else>created</span> Permission </div> <button @@ -256,9 +313,26 @@ onMounted(() => { :modal-label="modalLabel" v-on="{ 'hidden.bs.modal': modalClosed }" > - <template v-slot:header> Create new Permission </template> - <template v-slot:extra-button v-if="formState.readonly"> + <template v-slot:header v-if="formState.readonly" + >View Permission + </template> + <template v-slot:header v-else-if="props.editUserPermission !== undefined" + >Edit Permission + </template> + <template v-slot:header v-else> Create new Permission </template> + <template v-slot:extra-button> + <bootstrap-icon + v-if="props.deletable" + icon="trash-fill" + :height="15" + :width="15" + fill="currentColor" + class="me-2" + :class="{ 'delete-icon': !formState.loading }" + @click="deletePermission(permission.bucket_name, permission.uid)" + /> <bootstrap-icon + v-if="formState.readonly && props.editable" icon="pencil-fill" :height="15" :width="15" @@ -268,7 +342,10 @@ onMounted(() => { /> </template> <template v-slot:body> - <form @submit.prevent="formSubmit" id="permissionCreateForm"> + <form + @submit.prevent="formSubmit" + :id="'permissionCreateEditForm' + randomIDSuffix" + > <div class="mb-3 row"> <label for="bucketNameInput" class="col-2 col-form-label" >Bucket<span v-if="!formState.readonly">*</span></label @@ -400,12 +477,26 @@ onMounted(() => { > </template> <template v-slot:footer> - <button type="button" class="btn btn-secondary" data-bs-dismiss="modal"> + <button + v-if="backModalId !== undefined" + type="button" + class="btn btn-secondary" + :data-bs-target="'#' + props.backModalId" + data-bs-toggle="modal" + > + Back + </button> + <button + v-else + type="button" + class="btn btn-secondary" + data-bs-dismiss="modal" + > Close </button> <button type="submit" - form="permissionCreateForm" + :form="'permissionCreateEditForm' + randomIDSuffix" class="btn btn-primary" :disabled="formState.loading" v-if="!formState.readonly" @@ -430,4 +521,12 @@ onMounted(() => { .pseudo-link:hover { color: var(--bs-primary); } + +.delete-icon { + color: var(--bs-secondary); + cursor: pointer; +} +.delete-icon:hover { + color: var(--bs-danger); +} </style> diff --git a/src/views/object-storage/BucketsView.vue b/src/views/object-storage/BucketsView.vue index a3ae113cf64b4c0afa37b9f34af27dfcdbe2a98e..0fab4f55a2007c0b3c2103cd935e5ab6c16603b6 100644 --- a/src/views/object-storage/BucketsView.vue +++ b/src/views/object-storage/BucketsView.vue @@ -1,5 +1,6 @@ <script setup lang="ts"> -import { onMounted, reactive } from "vue"; +import { onMounted, reactive, computed } from "vue"; +import type { ComputedRef } from "vue"; import type { BucketOut, BucketPermission } from "@/client"; import { BucketService, BucketPermissionsService } from "@/client"; import { useRoute, useRouter } from "vue-router"; @@ -43,19 +44,31 @@ function fetchBuckets() { } } +const currentPermission: ComputedRef<BucketPermission | undefined> = computed( + () => { + return bucketsState.permissions[route.params.bucketName as string]; + } +); + function addBucket(bucket: BucketOut) { bucketsState.buckets.push(bucket); } function deleteBucket(bucketName: string) { BucketService.bucketDeleteBucket(bucketName).then(() => { - router.push({ name: "buckets" }); - bucketsState.buckets = bucketsState.buckets.filter( - (bucket) => bucket.name !== bucketName - ); + bucketDeleted(bucketName); }); } +function bucketDeleted(bucketName: string) { + if (bucketsState.buckets.map((bucket) => bucket.name).includes(bucketName)) { + router.push({ name: "buckets" }); + } + bucketsState.buckets = bucketsState.buckets.filter( + (bucket) => bucket.name !== bucketName + ); +} + onMounted(() => { fetchBuckets(); }); @@ -106,7 +119,7 @@ onMounted(() => { /> </div> - <div class="list-group overflow-scroll mt-3"> + <div class="list-group mt-3"> <div v-if="!bucketsState.loading"> <bucket-list-item v-for="bucket in bucketsState.buckets" @@ -117,7 +130,9 @@ onMounted(() => { " :bucket="bucket" :loading="false" + :permission="bucketsState.permissions[bucket.name]" @delete-bucket="deleteBucket" + @permission-deleted="(bucketName) => bucketDeleted(bucketName)" /> </div> <div v-else> @@ -126,13 +141,14 @@ onMounted(() => { :key="n" :active="false" :loading="true" + :permission="undefined" :bucket="{ name: '', description: '', created_at: '', owner: '' }" ></bucket-list-item> </div> </div> </div> <div class="col-9"> - <router-view></router-view> + <router-view :permission="currentPermission"></router-view> </div> </div> </template>