diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index b405b47cb6be68df9ed8e7d7941244f2cc22f643..cab254a8cead5777e77d2180acf280bf8cdfdbab 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -1,4 +1,4 @@
-image: node:16
+image: node:18
 cache:
   paths:
     - node_modules
diff --git a/Dockerfile b/Dockerfile
index a4eba23cfd2e33d53d8ed21c5b0c529bb034a520..831ee92da98a9c44ebee34cd9920c5db395bbf85 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,5 +1,5 @@
 # build stage
-FROM node:16 as build-stage
+FROM node:18 as build-stage
 WORKDIR /app
 HEALTHCHECK --interval=35s --timeout=4s CMD curl -f http://localhost || exit 1
 # RUN apk add yarn
@@ -11,7 +11,7 @@ RUN npm run build-only
 
 # production stage
 FROM nginx:stable-alpine as production-stage
-HEALTHCHECK --interval=35s --timeout=4s CMD curl --head -f http://localhost || exit 1
+HEALTHCHECK --interval=305s --timeout=4s CMD curl --head -f http://localhost || exit 1
 COPY --from=build-stage /app/dist /usr/share/nginx/html
 COPY --from=build-stage /app/src/assets/env.template.js /tmp
 COPY nginx.conf /etc/nginx/conf.d/default.conf
diff --git a/index.html b/index.html
index 482eda96a495b02a952cbced079a488220542251..a39f962f8a08306b6fe930a84d29aa9f1b8d0cab 100644
--- a/index.html
+++ b/index.html
@@ -1,5 +1,5 @@
 <!DOCTYPE html>
-<html lang="en">
+<html lang="en" data-bs-theme="light">
   <head>
     <meta charset="UTF-8" />
     <link rel="icon" href="/favicon.ico" />
diff --git a/package-lock.json b/package-lock.json
index 75e4957e6d01c0f0c5b50f7a248a554cf5126249..b3a54f3c78f2e2fb405ee781cd260f03b86e8d53 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -11,46 +11,47 @@
         "@aws-sdk/client-s3": "^3.379.1",
         "@aws-sdk/lib-storage": "^3.379.1",
         "@aws-sdk/s3-request-presigner": "^3.379.1",
-        "@fortawesome/fontawesome-free": "^6.4.0",
-        "@popperjs/core": "^2.11.8",
-        "ajv": "^8.12.0",
-        "bootstrap": "^5.2.3",
-        "chart.js": "^4.3.2",
-        "chartjs-plugin-zoom": "^2.0.1",
-        "dayjs": "^1.11.9",
-        "dompurify": "^3.0.5",
-        "filesize": "^10.0.8",
-        "pinia": "^2.1.6",
-        "semver": "^7.5.4",
-        "showdown": "^2.1.0",
-        "vue": "^3.3.4",
-        "vue-router": "^4.2.4",
-        "vue3-cookies": "^1.0.6"
+        "@fortawesome/fontawesome-free": "~6.4.2",
+        "@popperjs/core": "~2.11.8",
+        "ajv": "~8.12.0",
+        "bootstrap": "~5.3.1",
+        "chart.js": "~4.3.3",
+        "chartjs-plugin-zoom": "~2.0.1",
+        "dayjs": "~1.11.9",
+        "dompurify": "~3.0.5",
+        "filesize": "~10.0.12",
+        "pinia": "~2.1.6",
+        "semver": "~7.5.4",
+        "showdown": "~2.1.0",
+        "vue": "~3.3.4",
+        "vue-router": "~4.2.4",
+        "vue3-cookies": "~1.0.6"
       },
       "devDependencies": {
-        "@esbuild-plugins/node-globals-polyfill": "^0.2.3",
-        "@esbuild-plugins/node-modules-polyfill": "^0.2.2",
-        "@rushstack/eslint-patch": "^1.2.0",
-        "@types/bootstrap": "^5.2.6",
-        "@types/dompurify": "^3.0.2",
-        "@types/node": "^16.11.45",
-        "@types/semver": "^7.5.0",
-        "@types/showdown": "^2.0.1",
-        "@vitejs/plugin-vue": "^4.2.3",
-        "@vue/eslint-config-prettier": "^7.1.0",
-        "@vue/eslint-config-typescript": "^11.0.2",
-        "@vue/tsconfig": "^0.1.3",
-        "axios": "^1.4.0",
-        "eslint": "^8.46.0",
-        "eslint-plugin-vue": "^9.16.1",
-        "npm-run-all": "^4.1.5",
+        "@esbuild-plugins/node-globals-polyfill": "~0.2.3",
+        "@esbuild-plugins/node-modules-polyfill": "~0.2.2",
+        "@rushstack/eslint-patch": "~1.2.0",
+        "@tsconfig/node18": "^18.2.1",
+        "@types/bootstrap": "~5.2.6",
+        "@types/dompurify": "~3.0.2",
+        "@types/node": "^16.18.48",
+        "@types/semver": "~7.5.1",
+        "@types/showdown": "~2.0.1",
+        "@vitejs/plugin-vue": "~4.3.4",
+        "@vue/eslint-config-prettier": "~8.0.0",
+        "@vue/eslint-config-typescript": "~11.0.3",
+        "@vue/tsconfig": "~0.4.0",
+        "axios": "~1.5.0",
+        "eslint": "~8.48.0",
+        "eslint-plugin-vue": "~9.17.0",
+        "npm-run-all": "~4.1.5",
         "openapi-typescript-codegen": "^0.25.0",
-        "prettier": "^2.8.4",
-        "rollup-plugin-node-polyfills": "^0.2.1",
-        "sass": "^1.59.3",
-        "typescript": "~4.9.5",
-        "vite": "^4.2.3",
-        "vue-tsc": "^1.8.8"
+        "prettier": "~3.0.3",
+        "rollup-plugin-node-polyfills": "~0.2.1",
+        "sass": "~1.66.1",
+        "typescript": "~5.1.6",
+        "vite": "~4.4.9",
+        "vue-tsc": "~1.8.10"
       }
     },
     "node_modules/@aashutoshrathi/word-wrap": {
@@ -1272,9 +1273,9 @@
       }
     },
     "node_modules/@eslint/eslintrc": {
-      "version": "2.1.1",
-      "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.1.tgz",
-      "integrity": "sha512-9t7ZA7NGGK8ckelF0PQCfcxIUzs1Md5rrO6U/c+FIQNanea5UZC0wqKXH4vHBccmu4ZJgZ2idtPeW7+Q2npOEA==",
+      "version": "2.1.2",
+      "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.2.tgz",
+      "integrity": "sha512-+wvgpDsrB1YqAMdEUCcnTlpfVBH7Vqn6A/NT3D8WVXFIaKMlErPIZT3oCIAVCOtarRpMtelZLqJeU3t7WY6X6g==",
       "dev": true,
       "dependencies": {
         "ajv": "^6.12.4",
@@ -1317,18 +1318,18 @@
       "dev": true
     },
     "node_modules/@eslint/js": {
-      "version": "8.46.0",
-      "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.46.0.tgz",
-      "integrity": "sha512-a8TLtmPi8xzPkCbp/OGFUo5yhRkHM2Ko9kOWP4znJr0WAhWyThaw3PnwX4vOTWOAMsV2uRt32PPDcEz63esSaA==",
+      "version": "8.48.0",
+      "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.48.0.tgz",
+      "integrity": "sha512-ZSjtmelB7IJfWD2Fvb7+Z+ChTIKWq6kjda95fLcQKNS5aheVHn4IkfgRQE3sIIzTcSLwLcLZUD9UBt+V7+h+Pw==",
       "dev": true,
       "engines": {
         "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
       }
     },
     "node_modules/@fortawesome/fontawesome-free": {
-      "version": "6.4.0",
-      "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-6.4.0.tgz",
-      "integrity": "sha512-0NyytTlPJwB/BF5LtRV8rrABDbe3TdTXqNB3PdZ+UUUZAEIrdOJdmABqKjt4AXwIoJNaRVVZEXxpNrqvE1GAYQ==",
+      "version": "6.4.2",
+      "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-6.4.2.tgz",
+      "integrity": "sha512-m5cPn3e2+FDCOgi1mz0RexTUvvQibBebOUlUlW0+YrMjDTPkiJ6VTKukA1GRsvRw+12KyJndNjj0O4AgTxm2Pg==",
       "hasInstallScript": true,
       "engines": {
         "node": ">=6"
@@ -1418,6 +1419,32 @@
         "node": ">= 8"
       }
     },
+    "node_modules/@pkgr/utils": {
+      "version": "2.4.2",
+      "resolved": "https://registry.npmjs.org/@pkgr/utils/-/utils-2.4.2.tgz",
+      "integrity": "sha512-POgTXhjrTfbTV63DiFXav4lBHiICLKKwDeaKn9Nphwj7WH6m0hMMCaJkMyRWjgtPFyRKRVoMXXjczsTQRDEhYw==",
+      "dev": true,
+      "dependencies": {
+        "cross-spawn": "^7.0.3",
+        "fast-glob": "^3.3.0",
+        "is-glob": "^4.0.3",
+        "open": "^9.1.0",
+        "picocolors": "^1.0.0",
+        "tslib": "^2.6.0"
+      },
+      "engines": {
+        "node": "^12.20.0 || ^14.18.0 || >=16.0.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/unts"
+      }
+    },
+    "node_modules/@pkgr/utils/node_modules/tslib": {
+      "version": "2.6.2",
+      "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz",
+      "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==",
+      "dev": true
+    },
     "node_modules/@popperjs/core": {
       "version": "2.11.8",
       "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz",
@@ -2024,6 +2051,12 @@
         "node": ">=14.0.0"
       }
     },
+    "node_modules/@tsconfig/node18": {
+      "version": "18.2.1",
+      "resolved": "https://registry.npmjs.org/@tsconfig/node18/-/node18-18.2.1.tgz",
+      "integrity": "sha512-RDDZFuofwkcKpl8Vpj5wFbY+H53xOtqK7ckEL1sXsbPwvKwDdjQf3LkHbtt9sxIHn9nWIEwkmCwBRZ6z5TKU2A==",
+      "dev": true
+    },
     "node_modules/@types/bootstrap": {
       "version": "5.2.6",
       "resolved": "https://registry.npmjs.org/@types/bootstrap/-/bootstrap-5.2.6.tgz",
@@ -2049,15 +2082,15 @@
       "dev": true
     },
     "node_modules/@types/node": {
-      "version": "16.18.18",
-      "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.18.tgz",
-      "integrity": "sha512-fwGw1uvQAzabxL1pyoknPlJIF2t7+K90uTqynleKRx24n3lYcxWa3+KByLhgkF8GEAK2c7hC8Ki0RkNM5H15jQ==",
+      "version": "16.18.48",
+      "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.48.tgz",
+      "integrity": "sha512-mlaecDKQ7rIZrYD7iiKNdzFb6e/qD5I9U1rAhq+Fd+DWvYVs+G2kv74UFHmSOlg5+i/vF3XxuR522V4u8BqO+Q==",
       "dev": true
     },
     "node_modules/@types/semver": {
-      "version": "7.5.0",
-      "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.0.tgz",
-      "integrity": "sha512-G8hZ6XJiHnuhQKR7ZmysCeJWE08o8T0AXtk5darsCaTVsYZhhgUrq53jizaR2FvsoeCwJhlmwTjkXBY5Pn/ZHw==",
+      "version": "7.5.1",
+      "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.1.tgz",
+      "integrity": "sha512-cJRQXpObxfNKkFAZbJl2yjWtJCqELQIdShsogr1d2MilP8dKD9TE/nEKHkJgUNHdGKCQaf9HbIynuV2csLGVLg==",
       "dev": true
     },
     "node_modules/@types/showdown": {
@@ -2073,17 +2106,17 @@
       "dev": true
     },
     "node_modules/@typescript-eslint/eslint-plugin": {
-      "version": "5.56.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.56.0.tgz",
-      "integrity": "sha512-ZNW37Ccl3oMZkzxrYDUX4o7cnuPgU+YrcaYXzsRtLB16I1FR5SHMqga3zGsaSliZADCWo2v8qHWqAYIj8nWCCg==",
+      "version": "5.62.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.62.0.tgz",
+      "integrity": "sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag==",
       "dev": true,
       "dependencies": {
         "@eslint-community/regexpp": "^4.4.0",
-        "@typescript-eslint/scope-manager": "5.56.0",
-        "@typescript-eslint/type-utils": "5.56.0",
-        "@typescript-eslint/utils": "5.56.0",
+        "@typescript-eslint/scope-manager": "5.62.0",
+        "@typescript-eslint/type-utils": "5.62.0",
+        "@typescript-eslint/utils": "5.62.0",
         "debug": "^4.3.4",
-        "grapheme-splitter": "^1.0.4",
+        "graphemer": "^1.4.0",
         "ignore": "^5.2.0",
         "natural-compare-lite": "^1.4.0",
         "semver": "^7.3.7",
@@ -2107,14 +2140,14 @@
       }
     },
     "node_modules/@typescript-eslint/parser": {
-      "version": "5.56.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.56.0.tgz",
-      "integrity": "sha512-sn1OZmBxUsgxMmR8a8U5QM/Wl+tyqlH//jTqCg8daTAmhAk26L2PFhcqPLlYBhYUJMZJK276qLXlHN3a83o2cg==",
+      "version": "5.62.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.62.0.tgz",
+      "integrity": "sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==",
       "dev": true,
       "dependencies": {
-        "@typescript-eslint/scope-manager": "5.56.0",
-        "@typescript-eslint/types": "5.56.0",
-        "@typescript-eslint/typescript-estree": "5.56.0",
+        "@typescript-eslint/scope-manager": "5.62.0",
+        "@typescript-eslint/types": "5.62.0",
+        "@typescript-eslint/typescript-estree": "5.62.0",
         "debug": "^4.3.4"
       },
       "engines": {
@@ -2134,13 +2167,13 @@
       }
     },
     "node_modules/@typescript-eslint/scope-manager": {
-      "version": "5.56.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.56.0.tgz",
-      "integrity": "sha512-jGYKyt+iBakD0SA5Ww8vFqGpoV2asSjwt60Gl6YcO8ksQ8s2HlUEyHBMSa38bdLopYqGf7EYQMUIGdT/Luw+sw==",
+      "version": "5.62.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz",
+      "integrity": "sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w==",
       "dev": true,
       "dependencies": {
-        "@typescript-eslint/types": "5.56.0",
-        "@typescript-eslint/visitor-keys": "5.56.0"
+        "@typescript-eslint/types": "5.62.0",
+        "@typescript-eslint/visitor-keys": "5.62.0"
       },
       "engines": {
         "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
@@ -2151,13 +2184,13 @@
       }
     },
     "node_modules/@typescript-eslint/type-utils": {
-      "version": "5.56.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.56.0.tgz",
-      "integrity": "sha512-8WxgOgJjWRy6m4xg9KoSHPzBNZeQbGlQOH7l2QEhQID/+YseaFxg5J/DLwWSsi9Axj4e/cCiKx7PVzOq38tY4A==",
+      "version": "5.62.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.62.0.tgz",
+      "integrity": "sha512-xsSQreu+VnfbqQpW5vnCJdq1Z3Q0U31qiWmRhr98ONQmcp/yhiPJFPq8MXiJVLiksmOKSjIldZzkebzHuCGzew==",
       "dev": true,
       "dependencies": {
-        "@typescript-eslint/typescript-estree": "5.56.0",
-        "@typescript-eslint/utils": "5.56.0",
+        "@typescript-eslint/typescript-estree": "5.62.0",
+        "@typescript-eslint/utils": "5.62.0",
         "debug": "^4.3.4",
         "tsutils": "^3.21.0"
       },
@@ -2178,9 +2211,9 @@
       }
     },
     "node_modules/@typescript-eslint/types": {
-      "version": "5.56.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.56.0.tgz",
-      "integrity": "sha512-JyAzbTJcIyhuUhogmiu+t79AkdnqgPUEsxMTMc/dCZczGMJQh1MK2wgrju++yMN6AWroVAy2jxyPcPr3SWCq5w==",
+      "version": "5.62.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.62.0.tgz",
+      "integrity": "sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==",
       "dev": true,
       "engines": {
         "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
@@ -2191,13 +2224,13 @@
       }
     },
     "node_modules/@typescript-eslint/typescript-estree": {
-      "version": "5.56.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.56.0.tgz",
-      "integrity": "sha512-41CH/GncsLXOJi0jb74SnC7jVPWeVJ0pxQj8bOjH1h2O26jXN3YHKDT1ejkVz5YeTEQPeLCCRY0U2r68tfNOcg==",
+      "version": "5.62.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz",
+      "integrity": "sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==",
       "dev": true,
       "dependencies": {
-        "@typescript-eslint/types": "5.56.0",
-        "@typescript-eslint/visitor-keys": "5.56.0",
+        "@typescript-eslint/types": "5.62.0",
+        "@typescript-eslint/visitor-keys": "5.62.0",
         "debug": "^4.3.4",
         "globby": "^11.1.0",
         "is-glob": "^4.0.3",
@@ -2218,17 +2251,17 @@
       }
     },
     "node_modules/@typescript-eslint/utils": {
-      "version": "5.56.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.56.0.tgz",
-      "integrity": "sha512-XhZDVdLnUJNtbzaJeDSCIYaM+Tgr59gZGbFuELgF7m0IY03PlciidS7UQNKLE0+WpUTn1GlycEr6Ivb/afjbhA==",
+      "version": "5.62.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.62.0.tgz",
+      "integrity": "sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==",
       "dev": true,
       "dependencies": {
         "@eslint-community/eslint-utils": "^4.2.0",
         "@types/json-schema": "^7.0.9",
         "@types/semver": "^7.3.12",
-        "@typescript-eslint/scope-manager": "5.56.0",
-        "@typescript-eslint/types": "5.56.0",
-        "@typescript-eslint/typescript-estree": "5.56.0",
+        "@typescript-eslint/scope-manager": "5.62.0",
+        "@typescript-eslint/types": "5.62.0",
+        "@typescript-eslint/typescript-estree": "5.62.0",
         "eslint-scope": "^5.1.1",
         "semver": "^7.3.7"
       },
@@ -2244,12 +2277,12 @@
       }
     },
     "node_modules/@typescript-eslint/visitor-keys": {
-      "version": "5.56.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.56.0.tgz",
-      "integrity": "sha512-1mFdED7u5bZpX6Xxf5N9U2c18sb+8EvU3tyOIj6LQZ5OOvnmj8BVeNNP603OFPm5KkS1a7IvCIcwrdHXaEMG/Q==",
+      "version": "5.62.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz",
+      "integrity": "sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==",
       "dev": true,
       "dependencies": {
-        "@typescript-eslint/types": "5.56.0",
+        "@typescript-eslint/types": "5.62.0",
         "eslint-visitor-keys": "^3.3.0"
       },
       "engines": {
@@ -2261,9 +2294,9 @@
       }
     },
     "node_modules/@vitejs/plugin-vue": {
-      "version": "4.2.3",
-      "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-4.2.3.tgz",
-      "integrity": "sha512-R6JDUfiZbJA9cMiguQ7jxALsgiprjBeHL5ikpXfJCH62pPHtI+JdJ5xWj6Ev73yXSlYl86+blXn1kZHQ7uElxw==",
+      "version": "4.3.4",
+      "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-4.3.4.tgz",
+      "integrity": "sha512-ciXNIHKPriERBisHFBvnTbfKa6r9SAesOYXeGDzgegcvy9Q4xdScSHAmKbNT0M3O0S9LKhIf5/G+UYG4NnnzYw==",
       "dev": true,
       "engines": {
         "node": "^14.18.0 || >=16.0.0"
@@ -2274,30 +2307,30 @@
       }
     },
     "node_modules/@volar/language-core": {
-      "version": "1.10.0",
-      "resolved": "https://registry.npmjs.org/@volar/language-core/-/language-core-1.10.0.tgz",
-      "integrity": "sha512-ddyWwSYqcbEZNFHm+Z3NZd6M7Ihjcwl/9B5cZd8kECdimVXUFdFi60XHWD27nrWtUQIsUYIG7Ca1WBwV2u2LSQ==",
+      "version": "1.10.1",
+      "resolved": "https://registry.npmjs.org/@volar/language-core/-/language-core-1.10.1.tgz",
+      "integrity": "sha512-JnsM1mIPdfGPxmoOcK1c7HYAsL6YOv0TCJ4aW3AXPZN/Jb4R77epDyMZIVudSGjWMbvv/JfUa+rQ+dGKTmgwBA==",
       "dev": true,
       "dependencies": {
-        "@volar/source-map": "1.10.0"
+        "@volar/source-map": "1.10.1"
       }
     },
     "node_modules/@volar/source-map": {
-      "version": "1.10.0",
-      "resolved": "https://registry.npmjs.org/@volar/source-map/-/source-map-1.10.0.tgz",
-      "integrity": "sha512-/ibWdcOzDGiq/GM1JU2eX8fH1bvAhl66hfe8yEgLEzg9txgr6qb5sQ/DEz5PcDL75tF5H5sCRRwn8Eu8ezi9mw==",
+      "version": "1.10.1",
+      "resolved": "https://registry.npmjs.org/@volar/source-map/-/source-map-1.10.1.tgz",
+      "integrity": "sha512-3/S6KQbqa7pGC8CxPrg69qHLpOvkiPHGJtWPkI/1AXCsktkJ6gIk/5z4hyuMp8Anvs6eS/Kvp/GZa3ut3votKA==",
       "dev": true,
       "dependencies": {
         "muggle-string": "^0.3.1"
       }
     },
     "node_modules/@volar/typescript": {
-      "version": "1.10.0",
-      "resolved": "https://registry.npmjs.org/@volar/typescript/-/typescript-1.10.0.tgz",
-      "integrity": "sha512-OtqGtFbUKYC0pLNIk3mHQp5xWnvL1CJIUc9VE39VdZ/oqpoBh5jKfb9uJ45Y4/oP/WYTrif/Uxl1k8VTPz66Gg==",
+      "version": "1.10.1",
+      "resolved": "https://registry.npmjs.org/@volar/typescript/-/typescript-1.10.1.tgz",
+      "integrity": "sha512-+iiO9yUSRHIYjlteT+QcdRq8b44qH19/eiUZtjNtuh6D9ailYM7DVR0zO2sEgJlvCaunw/CF9Ov2KooQBpR4VQ==",
       "dev": true,
       "dependencies": {
-        "@volar/language-core": "1.10.0"
+        "@volar/language-core": "1.10.1"
       }
     },
     "node_modules/@vue/compiler-core": {
@@ -2373,28 +2406,28 @@
       "integrity": "sha512-o9KfBeaBmCKl10usN4crU53fYtC1r7jJwdGKjPT24t348rHxgfpZ0xL3Xm/gLUYnc0oTp8LAmrxOeLyu6tbk2Q=="
     },
     "node_modules/@vue/eslint-config-prettier": {
-      "version": "7.1.0",
-      "resolved": "https://registry.npmjs.org/@vue/eslint-config-prettier/-/eslint-config-prettier-7.1.0.tgz",
-      "integrity": "sha512-Pv/lVr0bAzSIHLd9iz0KnvAr4GKyCEl+h52bc4e5yWuDVtLgFwycF7nrbWTAQAS+FU6q1geVd07lc6EWfJiWKQ==",
+      "version": "8.0.0",
+      "resolved": "https://registry.npmjs.org/@vue/eslint-config-prettier/-/eslint-config-prettier-8.0.0.tgz",
+      "integrity": "sha512-55dPqtC4PM/yBjhAr+yEw6+7KzzdkBuLmnhBrDfp4I48+wy+Giqqj9yUr5T2uD/BkBROjjmqnLZmXRdOx/VtQg==",
       "dev": true,
       "dependencies": {
-        "eslint-config-prettier": "^8.3.0",
-        "eslint-plugin-prettier": "^4.0.0"
+        "eslint-config-prettier": "^8.8.0",
+        "eslint-plugin-prettier": "^5.0.0"
       },
       "peerDependencies": {
-        "eslint": ">= 7.28.0",
-        "prettier": ">= 2.0.0"
+        "eslint": ">= 8.0.0",
+        "prettier": ">= 3.0.0"
       }
     },
     "node_modules/@vue/eslint-config-typescript": {
-      "version": "11.0.2",
-      "resolved": "https://registry.npmjs.org/@vue/eslint-config-typescript/-/eslint-config-typescript-11.0.2.tgz",
-      "integrity": "sha512-EiKud1NqlWmSapBFkeSrE994qpKx7/27uCGnhdqzllYDpQZroyX/O6bwjEpeuyKamvLbsGdO6PMR2faIf+zFnw==",
+      "version": "11.0.3",
+      "resolved": "https://registry.npmjs.org/@vue/eslint-config-typescript/-/eslint-config-typescript-11.0.3.tgz",
+      "integrity": "sha512-dkt6W0PX6H/4Xuxg/BlFj5xHvksjpSlVjtkQCpaYJBIEuKj2hOVU7r+TIe+ysCwRYFz/lGqvklntRkCAibsbPw==",
       "dev": true,
       "dependencies": {
-        "@typescript-eslint/eslint-plugin": "^5.0.0",
-        "@typescript-eslint/parser": "^5.0.0",
-        "vue-eslint-parser": "^9.0.0"
+        "@typescript-eslint/eslint-plugin": "^5.59.1",
+        "@typescript-eslint/parser": "^5.59.1",
+        "vue-eslint-parser": "^9.1.1"
       },
       "engines": {
         "node": "^14.17.0 || >=16.0.0"
@@ -2411,9 +2444,9 @@
       }
     },
     "node_modules/@vue/language-core": {
-      "version": "1.8.8",
-      "resolved": "https://registry.npmjs.org/@vue/language-core/-/language-core-1.8.8.tgz",
-      "integrity": "sha512-i4KMTuPazf48yMdYoebTkgSOJdFraE4pQf0B+FTOFkbB+6hAfjrSou/UmYWRsWyZV6r4Rc6DDZdI39CJwL0rWw==",
+      "version": "1.8.10",
+      "resolved": "https://registry.npmjs.org/@vue/language-core/-/language-core-1.8.10.tgz",
+      "integrity": "sha512-db8PtM4ZZr7SYNH30XpKxUYnUBYaTvcuJ4c2whKK04fuAjbtjAIZ2al5GzGEfUlesmvkpgdbiSviRXUxgD9Omw==",
       "dev": true,
       "dependencies": {
         "@volar/language-core": "~1.10.0",
@@ -2531,27 +2564,19 @@
       "integrity": "sha512-7OjdcV8vQ74eiz1TZLzZP4JwqM5fA94K6yntPS5Z25r9HDuGNzaGdgvwKYq6S+MxwF0TFRwe50fIR/MYnakdkQ=="
     },
     "node_modules/@vue/tsconfig": {
-      "version": "0.1.3",
-      "resolved": "https://registry.npmjs.org/@vue/tsconfig/-/tsconfig-0.1.3.tgz",
-      "integrity": "sha512-kQVsh8yyWPvHpb8gIc9l/HIDiiVUy1amynLNpCy8p+FoCiZXCo6fQos5/097MmnNZc9AtseDsCrfkhqCrJ8Olg==",
-      "dev": true,
-      "peerDependencies": {
-        "@types/node": "*"
-      },
-      "peerDependenciesMeta": {
-        "@types/node": {
-          "optional": true
-        }
-      }
+      "version": "0.4.0",
+      "resolved": "https://registry.npmjs.org/@vue/tsconfig/-/tsconfig-0.4.0.tgz",
+      "integrity": "sha512-CPuIReonid9+zOG/CGTT05FXrPYATEqoDGNrEaqS4hwcw5BUNM2FguC0mOwJD4Jr16UpRVl9N0pY3P+srIbqmg==",
+      "dev": true
     },
     "node_modules/@vue/typescript": {
-      "version": "1.8.8",
-      "resolved": "https://registry.npmjs.org/@vue/typescript/-/typescript-1.8.8.tgz",
-      "integrity": "sha512-jUnmMB6egu5wl342eaUH236v8tdcEPXXkPgj+eI/F6JwW/lb+yAU6U07ZbQ3MVabZRlupIlPESB7ajgAGixhow==",
+      "version": "1.8.10",
+      "resolved": "https://registry.npmjs.org/@vue/typescript/-/typescript-1.8.10.tgz",
+      "integrity": "sha512-vPSpTXMk4chYwvyTGjM891cKgnx2r6vtbdANOp2mRU31f4HYGyLrZBlGgiua7SaO2cLjUg8y91OipJe0t8OFhA==",
       "dev": true,
       "dependencies": {
         "@volar/typescript": "~1.10.0",
-        "@vue/language-core": "1.8.8"
+        "@vue/language-core": "1.8.10"
       }
     },
     "node_modules/acorn": {
@@ -2674,9 +2699,9 @@
       }
     },
     "node_modules/axios": {
-      "version": "1.4.0",
-      "resolved": "https://registry.npmjs.org/axios/-/axios-1.4.0.tgz",
-      "integrity": "sha512-S4XCWMEmzvo64T9GfvQDOXgYRDJ/wsSZc7Jvdgx5u1sd0JwsuPLqb3SYmusag+edF6ziyMensPVqLTSc1PiSEA==",
+      "version": "1.5.0",
+      "resolved": "https://registry.npmjs.org/axios/-/axios-1.5.0.tgz",
+      "integrity": "sha512-D4DdjDo5CY50Qms0qGQTTw6Q44jl7zRwY7bthds06pUGfChBCTcQs+N743eFWGEd6pRTMd6A+I87aWyFV5wiZQ==",
       "dev": true,
       "dependencies": {
         "follow-redirects": "^1.15.0",
@@ -2709,6 +2734,15 @@
         }
       ]
     },
+    "node_modules/big-integer": {
+      "version": "1.6.51",
+      "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.51.tgz",
+      "integrity": "sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg==",
+      "dev": true,
+      "engines": {
+        "node": ">=0.6"
+      }
+    },
     "node_modules/binary-extensions": {
       "version": "2.2.0",
       "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
@@ -2725,9 +2759,9 @@
       "dev": true
     },
     "node_modules/bootstrap": {
-      "version": "5.2.3",
-      "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.2.3.tgz",
-      "integrity": "sha512-cEKPM+fwb3cT8NzQZYEu4HilJ3anCrWqh3CHAok1p9jXqMPsPTBhU25fBckEJHJ/p+tTxTFTsFQGM+gaHpi3QQ==",
+      "version": "5.3.1",
+      "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.3.1.tgz",
+      "integrity": "sha512-jzwza3Yagduci2x0rr9MeFSORjcHpt0lRZukZPZQJT1Dth5qzV7XcgGqYzi39KGAVYR8QEDVoO0ubFKOxzMG+g==",
       "funding": [
         {
           "type": "github",
@@ -2739,7 +2773,7 @@
         }
       ],
       "peerDependencies": {
-        "@popperjs/core": "^2.11.6"
+        "@popperjs/core": "^2.11.8"
       }
     },
     "node_modules/bowser": {
@@ -2747,6 +2781,18 @@
       "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.11.0.tgz",
       "integrity": "sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA=="
     },
+    "node_modules/bplist-parser": {
+      "version": "0.2.0",
+      "resolved": "https://registry.npmjs.org/bplist-parser/-/bplist-parser-0.2.0.tgz",
+      "integrity": "sha512-z0M+byMThzQmD9NILRniCUXYsYpjwnlO8N5uCFaCqIOpqRsJCrQL9NK3JsD67CN5a08nF5oIL2bD6loTdHOuKw==",
+      "dev": true,
+      "dependencies": {
+        "big-integer": "^1.6.44"
+      },
+      "engines": {
+        "node": ">= 5.10.0"
+      }
+    },
     "node_modules/brace-expansion": {
       "version": "1.1.11",
       "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
@@ -2778,6 +2824,21 @@
         "ieee754": "^1.1.4"
       }
     },
+    "node_modules/bundle-name": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-3.0.0.tgz",
+      "integrity": "sha512-PKA4BeSvBpQKQ8iPOGCSiell+N8P+Tf1DlwqmYhpe2gAhKPHn8EYOxVT+ShuGmhg8lN8XiSlS80yiExKXrURlw==",
+      "dev": true,
+      "dependencies": {
+        "run-applescript": "^5.0.0"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
     "node_modules/call-bind": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz",
@@ -2835,9 +2896,9 @@
       }
     },
     "node_modules/chart.js": {
-      "version": "4.3.2",
-      "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.3.2.tgz",
-      "integrity": "sha512-pvQNyFOY1QmbmIr8oDORL16/FFivfxj8V26VFpFilMo4cNvkV5WXLJetDio365pd9gKUHGdirUTbqJfw8tr+Dg==",
+      "version": "4.3.3",
+      "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.3.3.tgz",
+      "integrity": "sha512-aTk7pBw+x6sQYhon/NR3ikfUJuym/LdgpTlgZRe2PaEhjUMKBKyNaFCMVRAyTEWYFNO7qRu7iQVqOw/OqzxZxQ==",
       "dependencies": {
         "@kurkle/color": "^0.3.0"
       },
@@ -3004,6 +3065,52 @@
       "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==",
       "dev": true
     },
+    "node_modules/default-browser": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-4.0.0.tgz",
+      "integrity": "sha512-wX5pXO1+BrhMkSbROFsyxUm0i/cJEScyNhA4PPxc41ICuv05ZZB/MX28s8aZx6xjmatvebIapF6hLEKEcpneUA==",
+      "dev": true,
+      "dependencies": {
+        "bundle-name": "^3.0.0",
+        "default-browser-id": "^3.0.0",
+        "execa": "^7.1.1",
+        "titleize": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=14.16"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/default-browser-id": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-3.0.0.tgz",
+      "integrity": "sha512-OZ1y3y0SqSICtE8DE4S8YOE9UZOJ8wO16fKWVP5J1Qz42kV9jcnMVFrEE/noXb/ss3Q4pZIH79kxofzyNNtUNA==",
+      "dev": true,
+      "dependencies": {
+        "bplist-parser": "^0.2.0",
+        "untildify": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/define-lazy-prop": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz",
+      "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==",
+      "dev": true,
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
     "node_modules/define-properties": {
       "version": "1.2.0",
       "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.0.tgz",
@@ -3196,15 +3303,15 @@
       }
     },
     "node_modules/eslint": {
-      "version": "8.46.0",
-      "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.46.0.tgz",
-      "integrity": "sha512-cIO74PvbW0qU8e0mIvk5IV3ToWdCq5FYG6gWPHHkx6gNdjlbAYvtfHmlCMXxjcoVaIdwy/IAt3+mDkZkfvb2Dg==",
+      "version": "8.48.0",
+      "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.48.0.tgz",
+      "integrity": "sha512-sb6DLeIuRXxeM1YljSe1KEx9/YYeZFQWcV8Rq9HfigmdDEugjLEVEa1ozDjL6YDjBpQHPJxJzze+alxi4T3OLg==",
       "dev": true,
       "dependencies": {
         "@eslint-community/eslint-utils": "^4.2.0",
         "@eslint-community/regexpp": "^4.6.1",
-        "@eslint/eslintrc": "^2.1.1",
-        "@eslint/js": "^8.46.0",
+        "@eslint/eslintrc": "^2.1.2",
+        "@eslint/js": "8.48.0",
         "@humanwhocodes/config-array": "^0.11.10",
         "@humanwhocodes/module-importer": "^1.0.1",
         "@nodelib/fs.walk": "^1.2.8",
@@ -3215,7 +3322,7 @@
         "doctrine": "^3.0.0",
         "escape-string-regexp": "^4.0.0",
         "eslint-scope": "^7.2.2",
-        "eslint-visitor-keys": "^3.4.2",
+        "eslint-visitor-keys": "^3.4.3",
         "espree": "^9.6.1",
         "esquery": "^1.4.2",
         "esutils": "^2.0.2",
@@ -3262,30 +3369,38 @@
       }
     },
     "node_modules/eslint-plugin-prettier": {
-      "version": "4.2.1",
-      "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-4.2.1.tgz",
-      "integrity": "sha512-f/0rXLXUt0oFYs8ra4w49wYZBG5GKZpAYsJSm6rnYL5uVDjd+zowwMwVZHnAjf4edNrKpCDYfXDgmRE/Ak7QyQ==",
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.0.0.tgz",
+      "integrity": "sha512-AgaZCVuYDXHUGxj/ZGu1u8H8CYgDY3iG6w5kUFw4AzMVXzB7VvbKgYR4nATIN+OvUrghMbiDLeimVjVY5ilq3w==",
       "dev": true,
       "dependencies": {
-        "prettier-linter-helpers": "^1.0.0"
+        "prettier-linter-helpers": "^1.0.0",
+        "synckit": "^0.8.5"
       },
       "engines": {
-        "node": ">=12.0.0"
+        "node": "^14.18.0 || >=16.0.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/prettier"
       },
       "peerDependencies": {
-        "eslint": ">=7.28.0",
-        "prettier": ">=2.0.0"
+        "@types/eslint": ">=8.0.0",
+        "eslint": ">=8.0.0",
+        "prettier": ">=3.0.0"
       },
       "peerDependenciesMeta": {
+        "@types/eslint": {
+          "optional": true
+        },
         "eslint-config-prettier": {
           "optional": true
         }
       }
     },
     "node_modules/eslint-plugin-vue": {
-      "version": "9.16.1",
-      "resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-9.16.1.tgz",
-      "integrity": "sha512-2FtnTqazA6aYONfDuOZTk0QzwhAwi7Z4+uJ7+GHeGxcKapjqWlDsRWDenvyG/utyOfAS5bVRmAG3cEWiYEz2bA==",
+      "version": "9.17.0",
+      "resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-9.17.0.tgz",
+      "integrity": "sha512-r7Bp79pxQk9I5XDP0k2dpUC7Ots3OSWgvGZNu3BxmKK6Zg7NgVtcOB6OCna5Kb9oQwJPl5hq183WD0SY5tZtIQ==",
       "dev": true,
       "dependencies": {
         "@eslint-community/eslint-utils": "^4.4.0",
@@ -3317,9 +3432,9 @@
       }
     },
     "node_modules/eslint-visitor-keys": {
-      "version": "3.4.2",
-      "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.2.tgz",
-      "integrity": "sha512-8drBzUEyZ2llkpCA67iYrgEssKDUu68V8ChqqOfFupIaG/LCVPUT+CoGJpT77zJprs4T/W7p07LP7zAIMuweVw==",
+      "version": "3.4.3",
+      "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
+      "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
       "dev": true,
       "engines": {
         "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
@@ -3466,21 +3581,44 @@
         "node": ">=0.8.x"
       }
     },
+    "node_modules/execa": {
+      "version": "7.2.0",
+      "resolved": "https://registry.npmjs.org/execa/-/execa-7.2.0.tgz",
+      "integrity": "sha512-UduyVP7TLB5IcAQl+OzLyLcS/l32W/GLg+AhHJ+ow40FOk2U3SAllPwR44v4vmdFwIWqpdwxxpQbF1n5ta9seA==",
+      "dev": true,
+      "dependencies": {
+        "cross-spawn": "^7.0.3",
+        "get-stream": "^6.0.1",
+        "human-signals": "^4.3.0",
+        "is-stream": "^3.0.0",
+        "merge-stream": "^2.0.0",
+        "npm-run-path": "^5.1.0",
+        "onetime": "^6.0.0",
+        "signal-exit": "^3.0.7",
+        "strip-final-newline": "^3.0.0"
+      },
+      "engines": {
+        "node": "^14.18.0 || ^16.14.0 || >=18.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/sindresorhus/execa?sponsor=1"
+      }
+    },
     "node_modules/fast-deep-equal": {
       "version": "3.1.3",
       "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
       "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="
     },
     "node_modules/fast-diff": {
-      "version": "1.2.0",
-      "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz",
-      "integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==",
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz",
+      "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==",
       "dev": true
     },
     "node_modules/fast-glob": {
-      "version": "3.2.12",
-      "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz",
-      "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==",
+      "version": "3.3.1",
+      "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz",
+      "integrity": "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==",
       "dev": true,
       "dependencies": {
         "@nodelib/fs.stat": "^2.0.2",
@@ -3560,9 +3698,9 @@
       }
     },
     "node_modules/filesize": {
-      "version": "10.0.8",
-      "resolved": "https://registry.npmjs.org/filesize/-/filesize-10.0.8.tgz",
-      "integrity": "sha512-/ylBrxZ1GAjgh2CEemKKLwTtmXfWqTtN1jRl6iqLwnMEucUX5cmaCCUPGstQOHVCcK9uYL6o1cPNakLQU2sasQ==",
+      "version": "10.0.12",
+      "resolved": "https://registry.npmjs.org/filesize/-/filesize-10.0.12.tgz",
+      "integrity": "sha512-6RS9gDchbn+qWmtV2uSjo5vmKizgfCQeb5jKmqx8HyzA3MoLqqyQxN+QcjkGBJt7FjJ9qFce67Auyya5rRRbpw==",
       "engines": {
         "node": ">= 10.4.0"
       }
@@ -3738,6 +3876,18 @@
         "url": "https://github.com/sponsors/ljharb"
       }
     },
+    "node_modules/get-stream": {
+      "version": "6.0.1",
+      "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz",
+      "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==",
+      "dev": true,
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
     "node_modules/get-symbol-description": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz",
@@ -3787,9 +3937,9 @@
       }
     },
     "node_modules/globals": {
-      "version": "13.20.0",
-      "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz",
-      "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==",
+      "version": "13.21.0",
+      "resolved": "https://registry.npmjs.org/globals/-/globals-13.21.0.tgz",
+      "integrity": "sha512-ybyme3s4yy/t/3s35bewwXKOf7cvzfreG2lH0lZl0JB7I4GxRP2ghxOK/Nb9EkRXdbBXZLfq/p/0W2JUONB/Gg==",
       "dev": true,
       "dependencies": {
         "type-fest": "^0.20.2"
@@ -3854,12 +4004,6 @@
       "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
       "dev": true
     },
-    "node_modules/grapheme-splitter": {
-      "version": "1.0.4",
-      "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz",
-      "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==",
-      "dev": true
-    },
     "node_modules/graphemer": {
       "version": "1.4.0",
       "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz",
@@ -3991,6 +4135,15 @@
       "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==",
       "dev": true
     },
+    "node_modules/human-signals": {
+      "version": "4.3.1",
+      "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-4.3.1.tgz",
+      "integrity": "sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ==",
+      "dev": true,
+      "engines": {
+        "node": ">=14.18.0"
+      }
+    },
     "node_modules/ieee754": {
       "version": "1.2.1",
       "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
@@ -4178,6 +4331,21 @@
         "url": "https://github.com/sponsors/ljharb"
       }
     },
+    "node_modules/is-docker": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz",
+      "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==",
+      "dev": true,
+      "bin": {
+        "is-docker": "cli.js"
+      },
+      "engines": {
+        "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
     "node_modules/is-extglob": {
       "version": "2.1.1",
       "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
@@ -4199,6 +4367,24 @@
         "node": ">=0.10.0"
       }
     },
+    "node_modules/is-inside-container": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz",
+      "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==",
+      "dev": true,
+      "dependencies": {
+        "is-docker": "^3.0.0"
+      },
+      "bin": {
+        "is-inside-container": "cli.js"
+      },
+      "engines": {
+        "node": ">=14.16"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
     "node_modules/is-negative-zero": {
       "version": "2.0.2",
       "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz",
@@ -4272,6 +4458,18 @@
         "url": "https://github.com/sponsors/ljharb"
       }
     },
+    "node_modules/is-stream": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz",
+      "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==",
+      "dev": true,
+      "engines": {
+        "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
     "node_modules/is-string": {
       "version": "1.0.7",
       "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz",
@@ -4333,6 +4531,33 @@
         "url": "https://github.com/sponsors/ljharb"
       }
     },
+    "node_modules/is-wsl": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz",
+      "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==",
+      "dev": true,
+      "dependencies": {
+        "is-docker": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/is-wsl/node_modules/is-docker": {
+      "version": "2.2.1",
+      "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz",
+      "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==",
+      "dev": true,
+      "bin": {
+        "is-docker": "cli.js"
+      },
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
     "node_modules/isexe": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
@@ -4477,6 +4702,12 @@
         "node": ">= 0.10.0"
       }
     },
+    "node_modules/merge-stream": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
+      "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==",
+      "dev": true
+    },
     "node_modules/merge2": {
       "version": "1.4.1",
       "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
@@ -4520,6 +4751,18 @@
         "node": ">= 0.6"
       }
     },
+    "node_modules/mimic-fn": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz",
+      "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==",
+      "dev": true,
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
     "node_modules/minimatch": {
       "version": "3.1.2",
       "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
@@ -4607,9 +4850,9 @@
       }
     },
     "node_modules/normalize-package-data/node_modules/semver": {
-      "version": "5.7.1",
-      "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
-      "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
+      "version": "5.7.2",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz",
+      "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==",
       "dev": true,
       "bin": {
         "semver": "bin/semver"
@@ -4734,9 +4977,9 @@
       }
     },
     "node_modules/npm-run-all/node_modules/semver": {
-      "version": "5.7.1",
-      "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
-      "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
+      "version": "5.7.2",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz",
+      "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==",
       "dev": true,
       "bin": {
         "semver": "bin/semver"
@@ -4787,6 +5030,33 @@
         "which": "bin/which"
       }
     },
+    "node_modules/npm-run-path": {
+      "version": "5.1.0",
+      "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.1.0.tgz",
+      "integrity": "sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==",
+      "dev": true,
+      "dependencies": {
+        "path-key": "^4.0.0"
+      },
+      "engines": {
+        "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/npm-run-path/node_modules/path-key": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz",
+      "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==",
+      "dev": true,
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
     "node_modules/nth-check": {
       "version": "2.1.1",
       "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz",
@@ -4844,6 +5114,39 @@
         "wrappy": "1"
       }
     },
+    "node_modules/onetime": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz",
+      "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==",
+      "dev": true,
+      "dependencies": {
+        "mimic-fn": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/open": {
+      "version": "9.1.0",
+      "resolved": "https://registry.npmjs.org/open/-/open-9.1.0.tgz",
+      "integrity": "sha512-OS+QTnw1/4vrf+9hh1jc1jnYjzSG4ttTBB8UxOwAnInG3Uo4ssetzC1ihqaIHjLJnA5GGlRl6QlZXOTQhRBUvg==",
+      "dev": true,
+      "dependencies": {
+        "default-browser": "^4.0.0",
+        "define-lazy-prop": "^3.0.0",
+        "is-inside-container": "^1.0.0",
+        "is-wsl": "^2.2.0"
+      },
+      "engines": {
+        "node": ">=14.16"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
     "node_modules/openapi-typescript-codegen": {
       "version": "0.25.0",
       "resolved": "https://registry.npmjs.org/openapi-typescript-codegen/-/openapi-typescript-codegen-0.25.0.tgz",
@@ -5121,15 +5424,15 @@
       }
     },
     "node_modules/prettier": {
-      "version": "2.8.6",
-      "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.6.tgz",
-      "integrity": "sha512-mtuzdiBbHwPEgl7NxWlqOkithPyp4VN93V7VeHVWBF+ad3I5avc0RVDT4oImXQy9H/AqxA2NSQH8pSxHW6FYbQ==",
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.0.3.tgz",
+      "integrity": "sha512-L/4pUDMxcNa8R/EthV08Zt42WBO4h1rarVtK0K+QJG0X187OLo7l699jWw0GKuwzkPQ//jMFA/8Xm6Fh3J/DAg==",
       "dev": true,
       "bin": {
-        "prettier": "bin-prettier.js"
+        "prettier": "bin/prettier.cjs"
       },
       "engines": {
-        "node": ">=10.13.0"
+        "node": ">=14"
       },
       "funding": {
         "url": "https://github.com/prettier/prettier?sponsor=1"
@@ -5309,9 +5612,9 @@
       }
     },
     "node_modules/rollup": {
-      "version": "3.27.0",
-      "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.27.0.tgz",
-      "integrity": "sha512-aOltLCrYZ0FhJDm7fCqwTjIUEVjWjcydKBV/Zeid6Mn8BWgDCUBBWT5beM5ieForYNo/1ZHuGJdka26kvQ3Gzg==",
+      "version": "3.29.0",
+      "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.29.0.tgz",
+      "integrity": "sha512-nszM8DINnx1vSS+TpbWKMkxem0CDWk3cSit/WWCBVs9/JZ1I/XLwOsiUglYuYReaeWWSsW9kge5zE5NZtf/a4w==",
       "dev": true,
       "bin": {
         "rollup": "dist/bin/rollup"
@@ -5354,6 +5657,110 @@
         "estree-walker": "^0.6.1"
       }
     },
+    "node_modules/run-applescript": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-5.0.0.tgz",
+      "integrity": "sha512-XcT5rBksx1QdIhlFOCtgZkB99ZEouFZ1E2Kc2LHqNW13U3/74YGdkQRmThTwxy4QIyookibDKYZOPqX//6BlAg==",
+      "dev": true,
+      "dependencies": {
+        "execa": "^5.0.0"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/run-applescript/node_modules/execa": {
+      "version": "5.1.1",
+      "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz",
+      "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==",
+      "dev": true,
+      "dependencies": {
+        "cross-spawn": "^7.0.3",
+        "get-stream": "^6.0.0",
+        "human-signals": "^2.1.0",
+        "is-stream": "^2.0.0",
+        "merge-stream": "^2.0.0",
+        "npm-run-path": "^4.0.1",
+        "onetime": "^5.1.2",
+        "signal-exit": "^3.0.3",
+        "strip-final-newline": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sindresorhus/execa?sponsor=1"
+      }
+    },
+    "node_modules/run-applescript/node_modules/human-signals": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz",
+      "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==",
+      "dev": true,
+      "engines": {
+        "node": ">=10.17.0"
+      }
+    },
+    "node_modules/run-applescript/node_modules/is-stream": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
+      "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/run-applescript/node_modules/mimic-fn": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
+      "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==",
+      "dev": true,
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/run-applescript/node_modules/npm-run-path": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz",
+      "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==",
+      "dev": true,
+      "dependencies": {
+        "path-key": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/run-applescript/node_modules/onetime": {
+      "version": "5.1.2",
+      "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz",
+      "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==",
+      "dev": true,
+      "dependencies": {
+        "mimic-fn": "^2.1.0"
+      },
+      "engines": {
+        "node": ">=6"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/run-applescript/node_modules/strip-final-newline": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz",
+      "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==",
+      "dev": true,
+      "engines": {
+        "node": ">=6"
+      }
+    },
     "node_modules/run-parallel": {
       "version": "1.2.0",
       "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
@@ -5411,9 +5818,9 @@
       }
     },
     "node_modules/sass": {
-      "version": "1.59.3",
-      "resolved": "https://registry.npmjs.org/sass/-/sass-1.59.3.tgz",
-      "integrity": "sha512-QCq98N3hX1jfTCoUAsF3eyGuXLsY7BCnCEg9qAact94Yc21npG2/mVOqoDvE0fCbWDqiM4WlcJQla0gWG2YlxQ==",
+      "version": "1.66.1",
+      "resolved": "https://registry.npmjs.org/sass/-/sass-1.66.1.tgz",
+      "integrity": "sha512-50c+zTsZOJVgFfTgwwEzkjA3/QACgdNsKueWPyAR0mRINIvLAStVQBbPg14iuqEQ74NPDbXzJARJ/O4SI1zftA==",
       "dev": true,
       "dependencies": {
         "chokidar": ">=3.0.0 <4.0.0",
@@ -5424,7 +5831,7 @@
         "sass": "sass.js"
       },
       "engines": {
-        "node": ">=12.0.0"
+        "node": ">=14.0.0"
       }
     },
     "node_modules/semver": {
@@ -5500,6 +5907,12 @@
         "url": "https://github.com/sponsors/ljharb"
       }
     },
+    "node_modules/signal-exit": {
+      "version": "3.0.7",
+      "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
+      "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
+      "dev": true
+    },
     "node_modules/slash": {
       "version": "3.0.0",
       "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
@@ -5665,6 +6078,18 @@
         "node": ">=4"
       }
     },
+    "node_modules/strip-final-newline": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz",
+      "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==",
+      "dev": true,
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
     "node_modules/strip-json-comments": {
       "version": "3.1.1",
       "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
@@ -5706,12 +6131,40 @@
         "url": "https://github.com/sponsors/ljharb"
       }
     },
+    "node_modules/synckit": {
+      "version": "0.8.5",
+      "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.8.5.tgz",
+      "integrity": "sha512-L1dapNV6vu2s/4Sputv8xGsCdAVlb5nRDMFU/E27D44l5U6cw1g0dGd45uLc+OXjNMmF4ntiMdCimzcjFKQI8Q==",
+      "dev": true,
+      "dependencies": {
+        "@pkgr/utils": "^2.3.1",
+        "tslib": "^2.5.0"
+      },
+      "engines": {
+        "node": "^14.18.0 || >=16.0.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/unts"
+      }
+    },
     "node_modules/text-table": {
       "version": "0.2.0",
       "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
       "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==",
       "dev": true
     },
+    "node_modules/titleize": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/titleize/-/titleize-3.0.0.tgz",
+      "integrity": "sha512-KxVu8EYHDPBdUYdKZdKtU2aj2XfEx9AfjXxE/Aj0vT06w2icA09Vus1rh6eSu1y01akYg6BjIK/hxyLJINoMLQ==",
+      "dev": true,
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
     "node_modules/to-regex-range": {
       "version": "5.0.1",
       "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
@@ -5789,16 +6242,16 @@
       }
     },
     "node_modules/typescript": {
-      "version": "4.9.5",
-      "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz",
-      "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==",
+      "version": "5.1.6",
+      "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.6.tgz",
+      "integrity": "sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==",
       "devOptional": true,
       "bin": {
         "tsc": "bin/tsc",
         "tsserver": "bin/tsserver"
       },
       "engines": {
-        "node": ">=4.2.0"
+        "node": ">=14.17"
       }
     },
     "node_modules/uglify-js": {
@@ -5838,6 +6291,15 @@
         "node": ">= 10.0.0"
       }
     },
+    "node_modules/untildify": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz",
+      "integrity": "sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
     "node_modules/uri-js": {
       "version": "4.4.1",
       "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
@@ -5870,14 +6332,14 @@
       }
     },
     "node_modules/vite": {
-      "version": "4.4.8",
-      "resolved": "https://registry.npmjs.org/vite/-/vite-4.4.8.tgz",
-      "integrity": "sha512-LONawOUUjxQridNWGQlNizfKH89qPigK36XhMI7COMGztz8KNY0JHim7/xDd71CZwGT4HtSRgI7Hy+RlhG0Gvg==",
+      "version": "4.4.9",
+      "resolved": "https://registry.npmjs.org/vite/-/vite-4.4.9.tgz",
+      "integrity": "sha512-2mbUn2LlUmNASWwSCNSJ/EG2HuSRTnVNaydp6vMCm5VIqJsjMfbIWtbH2kDuwUVW5mMUKKZvGPX/rqeqVvv1XA==",
       "dev": true,
       "dependencies": {
         "esbuild": "^0.18.10",
-        "postcss": "^8.4.26",
-        "rollup": "^3.25.2"
+        "postcss": "^8.4.27",
+        "rollup": "^3.27.1"
       },
       "bin": {
         "vite": "bin/vite.js"
@@ -6007,13 +6469,13 @@
       }
     },
     "node_modules/vue-tsc": {
-      "version": "1.8.8",
-      "resolved": "https://registry.npmjs.org/vue-tsc/-/vue-tsc-1.8.8.tgz",
-      "integrity": "sha512-bSydNFQsF7AMvwWsRXD7cBIXaNs/KSjvzWLymq/UtKE36697sboX4EccSHFVxvgdBlI1frYPc/VMKJNB7DFeDQ==",
+      "version": "1.8.10",
+      "resolved": "https://registry.npmjs.org/vue-tsc/-/vue-tsc-1.8.10.tgz",
+      "integrity": "sha512-ptpTFFDoHQgkWJF7i5iERxooiQzOGtG1uKTfmAUuS3qPuSQGq+Ky/S8BFHhnFGwoOxq/PjmGN2QSZEfg1rtzQA==",
       "dev": true,
       "dependencies": {
-        "@vue/language-core": "1.8.8",
-        "@vue/typescript": "1.8.8",
+        "@vue/language-core": "1.8.10",
+        "@vue/typescript": "1.8.10",
         "semver": "^7.3.8"
       },
       "bin": {
@@ -7018,9 +7480,9 @@
       "dev": true
     },
     "@eslint/eslintrc": {
-      "version": "2.1.1",
-      "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.1.tgz",
-      "integrity": "sha512-9t7ZA7NGGK8ckelF0PQCfcxIUzs1Md5rrO6U/c+FIQNanea5UZC0wqKXH4vHBccmu4ZJgZ2idtPeW7+Q2npOEA==",
+      "version": "2.1.2",
+      "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.2.tgz",
+      "integrity": "sha512-+wvgpDsrB1YqAMdEUCcnTlpfVBH7Vqn6A/NT3D8WVXFIaKMlErPIZT3oCIAVCOtarRpMtelZLqJeU3t7WY6X6g==",
       "dev": true,
       "requires": {
         "ajv": "^6.12.4",
@@ -7055,15 +7517,15 @@
       }
     },
     "@eslint/js": {
-      "version": "8.46.0",
-      "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.46.0.tgz",
-      "integrity": "sha512-a8TLtmPi8xzPkCbp/OGFUo5yhRkHM2Ko9kOWP4znJr0WAhWyThaw3PnwX4vOTWOAMsV2uRt32PPDcEz63esSaA==",
+      "version": "8.48.0",
+      "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.48.0.tgz",
+      "integrity": "sha512-ZSjtmelB7IJfWD2Fvb7+Z+ChTIKWq6kjda95fLcQKNS5aheVHn4IkfgRQE3sIIzTcSLwLcLZUD9UBt+V7+h+Pw==",
       "dev": true
     },
     "@fortawesome/fontawesome-free": {
-      "version": "6.4.0",
-      "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-6.4.0.tgz",
-      "integrity": "sha512-0NyytTlPJwB/BF5LtRV8rrABDbe3TdTXqNB3PdZ+UUUZAEIrdOJdmABqKjt4AXwIoJNaRVVZEXxpNrqvE1GAYQ=="
+      "version": "6.4.2",
+      "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-6.4.2.tgz",
+      "integrity": "sha512-m5cPn3e2+FDCOgi1mz0RexTUvvQibBebOUlUlW0+YrMjDTPkiJ6VTKukA1GRsvRw+12KyJndNjj0O4AgTxm2Pg=="
     },
     "@humanwhocodes/config-array": {
       "version": "0.11.10",
@@ -7130,6 +7592,28 @@
         "fastq": "^1.6.0"
       }
     },
+    "@pkgr/utils": {
+      "version": "2.4.2",
+      "resolved": "https://registry.npmjs.org/@pkgr/utils/-/utils-2.4.2.tgz",
+      "integrity": "sha512-POgTXhjrTfbTV63DiFXav4lBHiICLKKwDeaKn9Nphwj7WH6m0hMMCaJkMyRWjgtPFyRKRVoMXXjczsTQRDEhYw==",
+      "dev": true,
+      "requires": {
+        "cross-spawn": "^7.0.3",
+        "fast-glob": "^3.3.0",
+        "is-glob": "^4.0.3",
+        "open": "^9.1.0",
+        "picocolors": "^1.0.0",
+        "tslib": "^2.6.0"
+      },
+      "dependencies": {
+        "tslib": {
+          "version": "2.6.2",
+          "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz",
+          "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==",
+          "dev": true
+        }
+      }
+    },
     "@popperjs/core": {
       "version": "2.11.8",
       "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz",
@@ -7615,6 +8099,12 @@
         "tslib": "^2.5.0"
       }
     },
+    "@tsconfig/node18": {
+      "version": "18.2.1",
+      "resolved": "https://registry.npmjs.org/@tsconfig/node18/-/node18-18.2.1.tgz",
+      "integrity": "sha512-RDDZFuofwkcKpl8Vpj5wFbY+H53xOtqK7ckEL1sXsbPwvKwDdjQf3LkHbtt9sxIHn9nWIEwkmCwBRZ6z5TKU2A==",
+      "dev": true
+    },
     "@types/bootstrap": {
       "version": "5.2.6",
       "resolved": "https://registry.npmjs.org/@types/bootstrap/-/bootstrap-5.2.6.tgz",
@@ -7640,15 +8130,15 @@
       "dev": true
     },
     "@types/node": {
-      "version": "16.18.18",
-      "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.18.tgz",
-      "integrity": "sha512-fwGw1uvQAzabxL1pyoknPlJIF2t7+K90uTqynleKRx24n3lYcxWa3+KByLhgkF8GEAK2c7hC8Ki0RkNM5H15jQ==",
+      "version": "16.18.48",
+      "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.48.tgz",
+      "integrity": "sha512-mlaecDKQ7rIZrYD7iiKNdzFb6e/qD5I9U1rAhq+Fd+DWvYVs+G2kv74UFHmSOlg5+i/vF3XxuR522V4u8BqO+Q==",
       "dev": true
     },
     "@types/semver": {
-      "version": "7.5.0",
-      "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.0.tgz",
-      "integrity": "sha512-G8hZ6XJiHnuhQKR7ZmysCeJWE08o8T0AXtk5darsCaTVsYZhhgUrq53jizaR2FvsoeCwJhlmwTjkXBY5Pn/ZHw==",
+      "version": "7.5.1",
+      "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.1.tgz",
+      "integrity": "sha512-cJRQXpObxfNKkFAZbJl2yjWtJCqELQIdShsogr1d2MilP8dKD9TE/nEKHkJgUNHdGKCQaf9HbIynuV2csLGVLg==",
       "dev": true
     },
     "@types/showdown": {
@@ -7664,17 +8154,17 @@
       "dev": true
     },
     "@typescript-eslint/eslint-plugin": {
-      "version": "5.56.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.56.0.tgz",
-      "integrity": "sha512-ZNW37Ccl3oMZkzxrYDUX4o7cnuPgU+YrcaYXzsRtLB16I1FR5SHMqga3zGsaSliZADCWo2v8qHWqAYIj8nWCCg==",
+      "version": "5.62.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.62.0.tgz",
+      "integrity": "sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag==",
       "dev": true,
       "requires": {
         "@eslint-community/regexpp": "^4.4.0",
-        "@typescript-eslint/scope-manager": "5.56.0",
-        "@typescript-eslint/type-utils": "5.56.0",
-        "@typescript-eslint/utils": "5.56.0",
+        "@typescript-eslint/scope-manager": "5.62.0",
+        "@typescript-eslint/type-utils": "5.62.0",
+        "@typescript-eslint/utils": "5.62.0",
         "debug": "^4.3.4",
-        "grapheme-splitter": "^1.0.4",
+        "graphemer": "^1.4.0",
         "ignore": "^5.2.0",
         "natural-compare-lite": "^1.4.0",
         "semver": "^7.3.7",
@@ -7682,53 +8172,53 @@
       }
     },
     "@typescript-eslint/parser": {
-      "version": "5.56.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.56.0.tgz",
-      "integrity": "sha512-sn1OZmBxUsgxMmR8a8U5QM/Wl+tyqlH//jTqCg8daTAmhAk26L2PFhcqPLlYBhYUJMZJK276qLXlHN3a83o2cg==",
+      "version": "5.62.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.62.0.tgz",
+      "integrity": "sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==",
       "dev": true,
       "requires": {
-        "@typescript-eslint/scope-manager": "5.56.0",
-        "@typescript-eslint/types": "5.56.0",
-        "@typescript-eslint/typescript-estree": "5.56.0",
+        "@typescript-eslint/scope-manager": "5.62.0",
+        "@typescript-eslint/types": "5.62.0",
+        "@typescript-eslint/typescript-estree": "5.62.0",
         "debug": "^4.3.4"
       }
     },
     "@typescript-eslint/scope-manager": {
-      "version": "5.56.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.56.0.tgz",
-      "integrity": "sha512-jGYKyt+iBakD0SA5Ww8vFqGpoV2asSjwt60Gl6YcO8ksQ8s2HlUEyHBMSa38bdLopYqGf7EYQMUIGdT/Luw+sw==",
+      "version": "5.62.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz",
+      "integrity": "sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w==",
       "dev": true,
       "requires": {
-        "@typescript-eslint/types": "5.56.0",
-        "@typescript-eslint/visitor-keys": "5.56.0"
+        "@typescript-eslint/types": "5.62.0",
+        "@typescript-eslint/visitor-keys": "5.62.0"
       }
     },
     "@typescript-eslint/type-utils": {
-      "version": "5.56.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.56.0.tgz",
-      "integrity": "sha512-8WxgOgJjWRy6m4xg9KoSHPzBNZeQbGlQOH7l2QEhQID/+YseaFxg5J/DLwWSsi9Axj4e/cCiKx7PVzOq38tY4A==",
+      "version": "5.62.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.62.0.tgz",
+      "integrity": "sha512-xsSQreu+VnfbqQpW5vnCJdq1Z3Q0U31qiWmRhr98ONQmcp/yhiPJFPq8MXiJVLiksmOKSjIldZzkebzHuCGzew==",
       "dev": true,
       "requires": {
-        "@typescript-eslint/typescript-estree": "5.56.0",
-        "@typescript-eslint/utils": "5.56.0",
+        "@typescript-eslint/typescript-estree": "5.62.0",
+        "@typescript-eslint/utils": "5.62.0",
         "debug": "^4.3.4",
         "tsutils": "^3.21.0"
       }
     },
     "@typescript-eslint/types": {
-      "version": "5.56.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.56.0.tgz",
-      "integrity": "sha512-JyAzbTJcIyhuUhogmiu+t79AkdnqgPUEsxMTMc/dCZczGMJQh1MK2wgrju++yMN6AWroVAy2jxyPcPr3SWCq5w==",
+      "version": "5.62.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.62.0.tgz",
+      "integrity": "sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==",
       "dev": true
     },
     "@typescript-eslint/typescript-estree": {
-      "version": "5.56.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.56.0.tgz",
-      "integrity": "sha512-41CH/GncsLXOJi0jb74SnC7jVPWeVJ0pxQj8bOjH1h2O26jXN3YHKDT1ejkVz5YeTEQPeLCCRY0U2r68tfNOcg==",
+      "version": "5.62.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz",
+      "integrity": "sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==",
       "dev": true,
       "requires": {
-        "@typescript-eslint/types": "5.56.0",
-        "@typescript-eslint/visitor-keys": "5.56.0",
+        "@typescript-eslint/types": "5.62.0",
+        "@typescript-eslint/visitor-keys": "5.62.0",
         "debug": "^4.3.4",
         "globby": "^11.1.0",
         "is-glob": "^4.0.3",
@@ -7737,63 +8227,63 @@
       }
     },
     "@typescript-eslint/utils": {
-      "version": "5.56.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.56.0.tgz",
-      "integrity": "sha512-XhZDVdLnUJNtbzaJeDSCIYaM+Tgr59gZGbFuELgF7m0IY03PlciidS7UQNKLE0+WpUTn1GlycEr6Ivb/afjbhA==",
+      "version": "5.62.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.62.0.tgz",
+      "integrity": "sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==",
       "dev": true,
       "requires": {
         "@eslint-community/eslint-utils": "^4.2.0",
         "@types/json-schema": "^7.0.9",
         "@types/semver": "^7.3.12",
-        "@typescript-eslint/scope-manager": "5.56.0",
-        "@typescript-eslint/types": "5.56.0",
-        "@typescript-eslint/typescript-estree": "5.56.0",
+        "@typescript-eslint/scope-manager": "5.62.0",
+        "@typescript-eslint/types": "5.62.0",
+        "@typescript-eslint/typescript-estree": "5.62.0",
         "eslint-scope": "^5.1.1",
         "semver": "^7.3.7"
       }
     },
     "@typescript-eslint/visitor-keys": {
-      "version": "5.56.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.56.0.tgz",
-      "integrity": "sha512-1mFdED7u5bZpX6Xxf5N9U2c18sb+8EvU3tyOIj6LQZ5OOvnmj8BVeNNP603OFPm5KkS1a7IvCIcwrdHXaEMG/Q==",
+      "version": "5.62.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz",
+      "integrity": "sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==",
       "dev": true,
       "requires": {
-        "@typescript-eslint/types": "5.56.0",
+        "@typescript-eslint/types": "5.62.0",
         "eslint-visitor-keys": "^3.3.0"
       }
     },
     "@vitejs/plugin-vue": {
-      "version": "4.2.3",
-      "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-4.2.3.tgz",
-      "integrity": "sha512-R6JDUfiZbJA9cMiguQ7jxALsgiprjBeHL5ikpXfJCH62pPHtI+JdJ5xWj6Ev73yXSlYl86+blXn1kZHQ7uElxw==",
+      "version": "4.3.4",
+      "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-4.3.4.tgz",
+      "integrity": "sha512-ciXNIHKPriERBisHFBvnTbfKa6r9SAesOYXeGDzgegcvy9Q4xdScSHAmKbNT0M3O0S9LKhIf5/G+UYG4NnnzYw==",
       "dev": true,
       "requires": {}
     },
     "@volar/language-core": {
-      "version": "1.10.0",
-      "resolved": "https://registry.npmjs.org/@volar/language-core/-/language-core-1.10.0.tgz",
-      "integrity": "sha512-ddyWwSYqcbEZNFHm+Z3NZd6M7Ihjcwl/9B5cZd8kECdimVXUFdFi60XHWD27nrWtUQIsUYIG7Ca1WBwV2u2LSQ==",
+      "version": "1.10.1",
+      "resolved": "https://registry.npmjs.org/@volar/language-core/-/language-core-1.10.1.tgz",
+      "integrity": "sha512-JnsM1mIPdfGPxmoOcK1c7HYAsL6YOv0TCJ4aW3AXPZN/Jb4R77epDyMZIVudSGjWMbvv/JfUa+rQ+dGKTmgwBA==",
       "dev": true,
       "requires": {
-        "@volar/source-map": "1.10.0"
+        "@volar/source-map": "1.10.1"
       }
     },
     "@volar/source-map": {
-      "version": "1.10.0",
-      "resolved": "https://registry.npmjs.org/@volar/source-map/-/source-map-1.10.0.tgz",
-      "integrity": "sha512-/ibWdcOzDGiq/GM1JU2eX8fH1bvAhl66hfe8yEgLEzg9txgr6qb5sQ/DEz5PcDL75tF5H5sCRRwn8Eu8ezi9mw==",
+      "version": "1.10.1",
+      "resolved": "https://registry.npmjs.org/@volar/source-map/-/source-map-1.10.1.tgz",
+      "integrity": "sha512-3/S6KQbqa7pGC8CxPrg69qHLpOvkiPHGJtWPkI/1AXCsktkJ6gIk/5z4hyuMp8Anvs6eS/Kvp/GZa3ut3votKA==",
       "dev": true,
       "requires": {
         "muggle-string": "^0.3.1"
       }
     },
     "@volar/typescript": {
-      "version": "1.10.0",
-      "resolved": "https://registry.npmjs.org/@volar/typescript/-/typescript-1.10.0.tgz",
-      "integrity": "sha512-OtqGtFbUKYC0pLNIk3mHQp5xWnvL1CJIUc9VE39VdZ/oqpoBh5jKfb9uJ45Y4/oP/WYTrif/Uxl1k8VTPz66Gg==",
+      "version": "1.10.1",
+      "resolved": "https://registry.npmjs.org/@volar/typescript/-/typescript-1.10.1.tgz",
+      "integrity": "sha512-+iiO9yUSRHIYjlteT+QcdRq8b44qH19/eiUZtjNtuh6D9ailYM7DVR0zO2sEgJlvCaunw/CF9Ov2KooQBpR4VQ==",
       "dev": true,
       "requires": {
-        "@volar/language-core": "1.10.0"
+        "@volar/language-core": "1.10.1"
       }
     },
     "@vue/compiler-core": {
@@ -7870,30 +8360,30 @@
       "integrity": "sha512-o9KfBeaBmCKl10usN4crU53fYtC1r7jJwdGKjPT24t348rHxgfpZ0xL3Xm/gLUYnc0oTp8LAmrxOeLyu6tbk2Q=="
     },
     "@vue/eslint-config-prettier": {
-      "version": "7.1.0",
-      "resolved": "https://registry.npmjs.org/@vue/eslint-config-prettier/-/eslint-config-prettier-7.1.0.tgz",
-      "integrity": "sha512-Pv/lVr0bAzSIHLd9iz0KnvAr4GKyCEl+h52bc4e5yWuDVtLgFwycF7nrbWTAQAS+FU6q1geVd07lc6EWfJiWKQ==",
+      "version": "8.0.0",
+      "resolved": "https://registry.npmjs.org/@vue/eslint-config-prettier/-/eslint-config-prettier-8.0.0.tgz",
+      "integrity": "sha512-55dPqtC4PM/yBjhAr+yEw6+7KzzdkBuLmnhBrDfp4I48+wy+Giqqj9yUr5T2uD/BkBROjjmqnLZmXRdOx/VtQg==",
       "dev": true,
       "requires": {
-        "eslint-config-prettier": "^8.3.0",
-        "eslint-plugin-prettier": "^4.0.0"
+        "eslint-config-prettier": "^8.8.0",
+        "eslint-plugin-prettier": "^5.0.0"
       }
     },
     "@vue/eslint-config-typescript": {
-      "version": "11.0.2",
-      "resolved": "https://registry.npmjs.org/@vue/eslint-config-typescript/-/eslint-config-typescript-11.0.2.tgz",
-      "integrity": "sha512-EiKud1NqlWmSapBFkeSrE994qpKx7/27uCGnhdqzllYDpQZroyX/O6bwjEpeuyKamvLbsGdO6PMR2faIf+zFnw==",
+      "version": "11.0.3",
+      "resolved": "https://registry.npmjs.org/@vue/eslint-config-typescript/-/eslint-config-typescript-11.0.3.tgz",
+      "integrity": "sha512-dkt6W0PX6H/4Xuxg/BlFj5xHvksjpSlVjtkQCpaYJBIEuKj2hOVU7r+TIe+ysCwRYFz/lGqvklntRkCAibsbPw==",
       "dev": true,
       "requires": {
-        "@typescript-eslint/eslint-plugin": "^5.0.0",
-        "@typescript-eslint/parser": "^5.0.0",
-        "vue-eslint-parser": "^9.0.0"
+        "@typescript-eslint/eslint-plugin": "^5.59.1",
+        "@typescript-eslint/parser": "^5.59.1",
+        "vue-eslint-parser": "^9.1.1"
       }
     },
     "@vue/language-core": {
-      "version": "1.8.8",
-      "resolved": "https://registry.npmjs.org/@vue/language-core/-/language-core-1.8.8.tgz",
-      "integrity": "sha512-i4KMTuPazf48yMdYoebTkgSOJdFraE4pQf0B+FTOFkbB+6hAfjrSou/UmYWRsWyZV6r4Rc6DDZdI39CJwL0rWw==",
+      "version": "1.8.10",
+      "resolved": "https://registry.npmjs.org/@vue/language-core/-/language-core-1.8.10.tgz",
+      "integrity": "sha512-db8PtM4ZZr7SYNH30XpKxUYnUBYaTvcuJ4c2whKK04fuAjbtjAIZ2al5GzGEfUlesmvkpgdbiSviRXUxgD9Omw==",
       "dev": true,
       "requires": {
         "@volar/language-core": "~1.10.0",
@@ -7995,20 +8485,19 @@
       "integrity": "sha512-7OjdcV8vQ74eiz1TZLzZP4JwqM5fA94K6yntPS5Z25r9HDuGNzaGdgvwKYq6S+MxwF0TFRwe50fIR/MYnakdkQ=="
     },
     "@vue/tsconfig": {
-      "version": "0.1.3",
-      "resolved": "https://registry.npmjs.org/@vue/tsconfig/-/tsconfig-0.1.3.tgz",
-      "integrity": "sha512-kQVsh8yyWPvHpb8gIc9l/HIDiiVUy1amynLNpCy8p+FoCiZXCo6fQos5/097MmnNZc9AtseDsCrfkhqCrJ8Olg==",
-      "dev": true,
-      "requires": {}
+      "version": "0.4.0",
+      "resolved": "https://registry.npmjs.org/@vue/tsconfig/-/tsconfig-0.4.0.tgz",
+      "integrity": "sha512-CPuIReonid9+zOG/CGTT05FXrPYATEqoDGNrEaqS4hwcw5BUNM2FguC0mOwJD4Jr16UpRVl9N0pY3P+srIbqmg==",
+      "dev": true
     },
     "@vue/typescript": {
-      "version": "1.8.8",
-      "resolved": "https://registry.npmjs.org/@vue/typescript/-/typescript-1.8.8.tgz",
-      "integrity": "sha512-jUnmMB6egu5wl342eaUH236v8tdcEPXXkPgj+eI/F6JwW/lb+yAU6U07ZbQ3MVabZRlupIlPESB7ajgAGixhow==",
+      "version": "1.8.10",
+      "resolved": "https://registry.npmjs.org/@vue/typescript/-/typescript-1.8.10.tgz",
+      "integrity": "sha512-vPSpTXMk4chYwvyTGjM891cKgnx2r6vtbdANOp2mRU31f4HYGyLrZBlGgiua7SaO2cLjUg8y91OipJe0t8OFhA==",
       "dev": true,
       "requires": {
         "@volar/typescript": "~1.10.0",
-        "@vue/language-core": "1.8.8"
+        "@vue/language-core": "1.8.10"
       }
     },
     "acorn": {
@@ -8095,9 +8584,9 @@
       "dev": true
     },
     "axios": {
-      "version": "1.4.0",
-      "resolved": "https://registry.npmjs.org/axios/-/axios-1.4.0.tgz",
-      "integrity": "sha512-S4XCWMEmzvo64T9GfvQDOXgYRDJ/wsSZc7Jvdgx5u1sd0JwsuPLqb3SYmusag+edF6ziyMensPVqLTSc1PiSEA==",
+      "version": "1.5.0",
+      "resolved": "https://registry.npmjs.org/axios/-/axios-1.5.0.tgz",
+      "integrity": "sha512-D4DdjDo5CY50Qms0qGQTTw6Q44jl7zRwY7bthds06pUGfChBCTcQs+N743eFWGEd6pRTMd6A+I87aWyFV5wiZQ==",
       "dev": true,
       "requires": {
         "follow-redirects": "^1.15.0",
@@ -8116,6 +8605,12 @@
       "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
       "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="
     },
+    "big-integer": {
+      "version": "1.6.51",
+      "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.51.tgz",
+      "integrity": "sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg==",
+      "dev": true
+    },
     "binary-extensions": {
       "version": "2.2.0",
       "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
@@ -8129,9 +8624,9 @@
       "dev": true
     },
     "bootstrap": {
-      "version": "5.2.3",
-      "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.2.3.tgz",
-      "integrity": "sha512-cEKPM+fwb3cT8NzQZYEu4HilJ3anCrWqh3CHAok1p9jXqMPsPTBhU25fBckEJHJ/p+tTxTFTsFQGM+gaHpi3QQ==",
+      "version": "5.3.1",
+      "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.3.1.tgz",
+      "integrity": "sha512-jzwza3Yagduci2x0rr9MeFSORjcHpt0lRZukZPZQJT1Dth5qzV7XcgGqYzi39KGAVYR8QEDVoO0ubFKOxzMG+g==",
       "requires": {}
     },
     "bowser": {
@@ -8139,6 +8634,15 @@
       "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.11.0.tgz",
       "integrity": "sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA=="
     },
+    "bplist-parser": {
+      "version": "0.2.0",
+      "resolved": "https://registry.npmjs.org/bplist-parser/-/bplist-parser-0.2.0.tgz",
+      "integrity": "sha512-z0M+byMThzQmD9NILRniCUXYsYpjwnlO8N5uCFaCqIOpqRsJCrQL9NK3JsD67CN5a08nF5oIL2bD6loTdHOuKw==",
+      "dev": true,
+      "requires": {
+        "big-integer": "^1.6.44"
+      }
+    },
     "brace-expansion": {
       "version": "1.1.11",
       "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
@@ -8167,6 +8671,15 @@
         "ieee754": "^1.1.4"
       }
     },
+    "bundle-name": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-3.0.0.tgz",
+      "integrity": "sha512-PKA4BeSvBpQKQ8iPOGCSiell+N8P+Tf1DlwqmYhpe2gAhKPHn8EYOxVT+ShuGmhg8lN8XiSlS80yiExKXrURlw==",
+      "dev": true,
+      "requires": {
+        "run-applescript": "^5.0.0"
+      }
+    },
     "call-bind": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz",
@@ -8206,9 +8719,9 @@
       }
     },
     "chart.js": {
-      "version": "4.3.2",
-      "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.3.2.tgz",
-      "integrity": "sha512-pvQNyFOY1QmbmIr8oDORL16/FFivfxj8V26VFpFilMo4cNvkV5WXLJetDio365pd9gKUHGdirUTbqJfw8tr+Dg==",
+      "version": "4.3.3",
+      "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.3.3.tgz",
+      "integrity": "sha512-aTk7pBw+x6sQYhon/NR3ikfUJuym/LdgpTlgZRe2PaEhjUMKBKyNaFCMVRAyTEWYFNO7qRu7iQVqOw/OqzxZxQ==",
       "requires": {
         "@kurkle/color": "^0.3.0"
       }
@@ -8331,6 +8844,34 @@
       "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==",
       "dev": true
     },
+    "default-browser": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-4.0.0.tgz",
+      "integrity": "sha512-wX5pXO1+BrhMkSbROFsyxUm0i/cJEScyNhA4PPxc41ICuv05ZZB/MX28s8aZx6xjmatvebIapF6hLEKEcpneUA==",
+      "dev": true,
+      "requires": {
+        "bundle-name": "^3.0.0",
+        "default-browser-id": "^3.0.0",
+        "execa": "^7.1.1",
+        "titleize": "^3.0.0"
+      }
+    },
+    "default-browser-id": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-3.0.0.tgz",
+      "integrity": "sha512-OZ1y3y0SqSICtE8DE4S8YOE9UZOJ8wO16fKWVP5J1Qz42kV9jcnMVFrEE/noXb/ss3Q4pZIH79kxofzyNNtUNA==",
+      "dev": true,
+      "requires": {
+        "bplist-parser": "^0.2.0",
+        "untildify": "^4.0.0"
+      }
+    },
+    "define-lazy-prop": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz",
+      "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==",
+      "dev": true
+    },
     "define-properties": {
       "version": "1.2.0",
       "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.0.tgz",
@@ -8480,15 +9021,15 @@
       "dev": true
     },
     "eslint": {
-      "version": "8.46.0",
-      "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.46.0.tgz",
-      "integrity": "sha512-cIO74PvbW0qU8e0mIvk5IV3ToWdCq5FYG6gWPHHkx6gNdjlbAYvtfHmlCMXxjcoVaIdwy/IAt3+mDkZkfvb2Dg==",
+      "version": "8.48.0",
+      "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.48.0.tgz",
+      "integrity": "sha512-sb6DLeIuRXxeM1YljSe1KEx9/YYeZFQWcV8Rq9HfigmdDEugjLEVEa1ozDjL6YDjBpQHPJxJzze+alxi4T3OLg==",
       "dev": true,
       "requires": {
         "@eslint-community/eslint-utils": "^4.2.0",
         "@eslint-community/regexpp": "^4.6.1",
-        "@eslint/eslintrc": "^2.1.1",
-        "@eslint/js": "^8.46.0",
+        "@eslint/eslintrc": "^2.1.2",
+        "@eslint/js": "8.48.0",
         "@humanwhocodes/config-array": "^0.11.10",
         "@humanwhocodes/module-importer": "^1.0.1",
         "@nodelib/fs.walk": "^1.2.8",
@@ -8499,7 +9040,7 @@
         "doctrine": "^3.0.0",
         "escape-string-regexp": "^4.0.0",
         "eslint-scope": "^7.2.2",
-        "eslint-visitor-keys": "^3.4.2",
+        "eslint-visitor-keys": "^3.4.3",
         "espree": "^9.6.1",
         "esquery": "^1.4.2",
         "esutils": "^2.0.2",
@@ -8568,18 +9109,19 @@
       "requires": {}
     },
     "eslint-plugin-prettier": {
-      "version": "4.2.1",
-      "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-4.2.1.tgz",
-      "integrity": "sha512-f/0rXLXUt0oFYs8ra4w49wYZBG5GKZpAYsJSm6rnYL5uVDjd+zowwMwVZHnAjf4edNrKpCDYfXDgmRE/Ak7QyQ==",
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.0.0.tgz",
+      "integrity": "sha512-AgaZCVuYDXHUGxj/ZGu1u8H8CYgDY3iG6w5kUFw4AzMVXzB7VvbKgYR4nATIN+OvUrghMbiDLeimVjVY5ilq3w==",
       "dev": true,
       "requires": {
-        "prettier-linter-helpers": "^1.0.0"
+        "prettier-linter-helpers": "^1.0.0",
+        "synckit": "^0.8.5"
       }
     },
     "eslint-plugin-vue": {
-      "version": "9.16.1",
-      "resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-9.16.1.tgz",
-      "integrity": "sha512-2FtnTqazA6aYONfDuOZTk0QzwhAwi7Z4+uJ7+GHeGxcKapjqWlDsRWDenvyG/utyOfAS5bVRmAG3cEWiYEz2bA==",
+      "version": "9.17.0",
+      "resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-9.17.0.tgz",
+      "integrity": "sha512-r7Bp79pxQk9I5XDP0k2dpUC7Ots3OSWgvGZNu3BxmKK6Zg7NgVtcOB6OCna5Kb9oQwJPl5hq183WD0SY5tZtIQ==",
       "dev": true,
       "requires": {
         "@eslint-community/eslint-utils": "^4.4.0",
@@ -8602,9 +9144,9 @@
       }
     },
     "eslint-visitor-keys": {
-      "version": "3.4.2",
-      "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.2.tgz",
-      "integrity": "sha512-8drBzUEyZ2llkpCA67iYrgEssKDUu68V8ChqqOfFupIaG/LCVPUT+CoGJpT77zJprs4T/W7p07LP7zAIMuweVw==",
+      "version": "3.4.3",
+      "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
+      "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
       "dev": true
     },
     "espree": {
@@ -8675,21 +9217,38 @@
       "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz",
       "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q=="
     },
+    "execa": {
+      "version": "7.2.0",
+      "resolved": "https://registry.npmjs.org/execa/-/execa-7.2.0.tgz",
+      "integrity": "sha512-UduyVP7TLB5IcAQl+OzLyLcS/l32W/GLg+AhHJ+ow40FOk2U3SAllPwR44v4vmdFwIWqpdwxxpQbF1n5ta9seA==",
+      "dev": true,
+      "requires": {
+        "cross-spawn": "^7.0.3",
+        "get-stream": "^6.0.1",
+        "human-signals": "^4.3.0",
+        "is-stream": "^3.0.0",
+        "merge-stream": "^2.0.0",
+        "npm-run-path": "^5.1.0",
+        "onetime": "^6.0.0",
+        "signal-exit": "^3.0.7",
+        "strip-final-newline": "^3.0.0"
+      }
+    },
     "fast-deep-equal": {
       "version": "3.1.3",
       "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
       "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="
     },
     "fast-diff": {
-      "version": "1.2.0",
-      "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz",
-      "integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==",
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz",
+      "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==",
       "dev": true
     },
     "fast-glob": {
-      "version": "3.2.12",
-      "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz",
-      "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==",
+      "version": "3.3.1",
+      "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz",
+      "integrity": "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==",
       "dev": true,
       "requires": {
         "@nodelib/fs.stat": "^2.0.2",
@@ -8749,9 +9308,9 @@
       }
     },
     "filesize": {
-      "version": "10.0.8",
-      "resolved": "https://registry.npmjs.org/filesize/-/filesize-10.0.8.tgz",
-      "integrity": "sha512-/ylBrxZ1GAjgh2CEemKKLwTtmXfWqTtN1jRl6iqLwnMEucUX5cmaCCUPGstQOHVCcK9uYL6o1cPNakLQU2sasQ=="
+      "version": "10.0.12",
+      "resolved": "https://registry.npmjs.org/filesize/-/filesize-10.0.12.tgz",
+      "integrity": "sha512-6RS9gDchbn+qWmtV2uSjo5vmKizgfCQeb5jKmqx8HyzA3MoLqqyQxN+QcjkGBJt7FjJ9qFce67Auyya5rRRbpw=="
     },
     "fill-range": {
       "version": "7.0.1",
@@ -8873,6 +9432,12 @@
         "has-symbols": "^1.0.3"
       }
     },
+    "get-stream": {
+      "version": "6.0.1",
+      "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz",
+      "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==",
+      "dev": true
+    },
     "get-symbol-description": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz",
@@ -8907,9 +9472,9 @@
       }
     },
     "globals": {
-      "version": "13.20.0",
-      "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz",
-      "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==",
+      "version": "13.21.0",
+      "resolved": "https://registry.npmjs.org/globals/-/globals-13.21.0.tgz",
+      "integrity": "sha512-ybyme3s4yy/t/3s35bewwXKOf7cvzfreG2lH0lZl0JB7I4GxRP2ghxOK/Nb9EkRXdbBXZLfq/p/0W2JUONB/Gg==",
       "dev": true,
       "requires": {
         "type-fest": "^0.20.2"
@@ -8953,12 +9518,6 @@
       "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
       "dev": true
     },
-    "grapheme-splitter": {
-      "version": "1.0.4",
-      "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz",
-      "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==",
-      "dev": true
-    },
     "graphemer": {
       "version": "1.4.0",
       "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz",
@@ -9046,6 +9605,12 @@
       "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==",
       "dev": true
     },
+    "human-signals": {
+      "version": "4.3.1",
+      "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-4.3.1.tgz",
+      "integrity": "sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ==",
+      "dev": true
+    },
     "ieee754": {
       "version": "1.2.1",
       "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
@@ -9174,6 +9739,12 @@
         "has-tostringtag": "^1.0.0"
       }
     },
+    "is-docker": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz",
+      "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==",
+      "dev": true
+    },
     "is-extglob": {
       "version": "2.1.1",
       "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
@@ -9189,6 +9760,15 @@
         "is-extglob": "^2.1.1"
       }
     },
+    "is-inside-container": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz",
+      "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==",
+      "dev": true,
+      "requires": {
+        "is-docker": "^3.0.0"
+      }
+    },
     "is-negative-zero": {
       "version": "2.0.2",
       "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz",
@@ -9235,6 +9815,12 @@
         "call-bind": "^1.0.2"
       }
     },
+    "is-stream": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz",
+      "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==",
+      "dev": true
+    },
     "is-string": {
       "version": "1.0.7",
       "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz",
@@ -9275,6 +9861,23 @@
         "call-bind": "^1.0.2"
       }
     },
+    "is-wsl": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz",
+      "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==",
+      "dev": true,
+      "requires": {
+        "is-docker": "^2.0.0"
+      },
+      "dependencies": {
+        "is-docker": {
+          "version": "2.2.1",
+          "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz",
+          "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==",
+          "dev": true
+        }
+      }
+    },
     "isexe": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
@@ -9392,6 +9995,12 @@
       "integrity": "sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==",
       "dev": true
     },
+    "merge-stream": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
+      "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==",
+      "dev": true
+    },
     "merge2": {
       "version": "1.4.1",
       "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
@@ -9423,6 +10032,12 @@
         "mime-db": "1.52.0"
       }
     },
+    "mimic-fn": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz",
+      "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==",
+      "dev": true
+    },
     "minimatch": {
       "version": "3.1.2",
       "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
@@ -9492,9 +10107,9 @@
       },
       "dependencies": {
         "semver": {
-          "version": "5.7.1",
-          "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
-          "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
+          "version": "5.7.2",
+          "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz",
+          "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==",
           "dev": true
         }
       }
@@ -9589,9 +10204,9 @@
           "dev": true
         },
         "semver": {
-          "version": "5.7.1",
-          "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
-          "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
+          "version": "5.7.2",
+          "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz",
+          "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==",
           "dev": true
         },
         "shebang-command": {
@@ -9629,6 +10244,23 @@
         }
       }
     },
+    "npm-run-path": {
+      "version": "5.1.0",
+      "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.1.0.tgz",
+      "integrity": "sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==",
+      "dev": true,
+      "requires": {
+        "path-key": "^4.0.0"
+      },
+      "dependencies": {
+        "path-key": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz",
+          "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==",
+          "dev": true
+        }
+      }
+    },
     "nth-check": {
       "version": "2.1.1",
       "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz",
@@ -9671,6 +10303,27 @@
         "wrappy": "1"
       }
     },
+    "onetime": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz",
+      "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==",
+      "dev": true,
+      "requires": {
+        "mimic-fn": "^4.0.0"
+      }
+    },
+    "open": {
+      "version": "9.1.0",
+      "resolved": "https://registry.npmjs.org/open/-/open-9.1.0.tgz",
+      "integrity": "sha512-OS+QTnw1/4vrf+9hh1jc1jnYjzSG4ttTBB8UxOwAnInG3Uo4ssetzC1ihqaIHjLJnA5GGlRl6QlZXOTQhRBUvg==",
+      "dev": true,
+      "requires": {
+        "default-browser": "^4.0.0",
+        "define-lazy-prop": "^3.0.0",
+        "is-inside-container": "^1.0.0",
+        "is-wsl": "^2.2.0"
+      }
+    },
     "openapi-typescript-codegen": {
       "version": "0.25.0",
       "resolved": "https://registry.npmjs.org/openapi-typescript-codegen/-/openapi-typescript-codegen-0.25.0.tgz",
@@ -9840,9 +10493,9 @@
       "dev": true
     },
     "prettier": {
-      "version": "2.8.6",
-      "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.6.tgz",
-      "integrity": "sha512-mtuzdiBbHwPEgl7NxWlqOkithPyp4VN93V7VeHVWBF+ad3I5avc0RVDT4oImXQy9H/AqxA2NSQH8pSxHW6FYbQ==",
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.0.3.tgz",
+      "integrity": "sha512-L/4pUDMxcNa8R/EthV08Zt42WBO4h1rarVtK0K+QJG0X187OLo7l699jWw0GKuwzkPQ//jMFA/8Xm6Fh3J/DAg==",
       "dev": true
     },
     "prettier-linter-helpers": {
@@ -9961,9 +10614,9 @@
       }
     },
     "rollup": {
-      "version": "3.27.0",
-      "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.27.0.tgz",
-      "integrity": "sha512-aOltLCrYZ0FhJDm7fCqwTjIUEVjWjcydKBV/Zeid6Mn8BWgDCUBBWT5beM5ieForYNo/1ZHuGJdka26kvQ3Gzg==",
+      "version": "3.29.0",
+      "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.29.0.tgz",
+      "integrity": "sha512-nszM8DINnx1vSS+TpbWKMkxem0CDWk3cSit/WWCBVs9/JZ1I/XLwOsiUglYuYReaeWWSsW9kge5zE5NZtf/a4w==",
       "dev": true,
       "requires": {
         "fsevents": "~2.3.2"
@@ -9998,6 +10651,76 @@
         "estree-walker": "^0.6.1"
       }
     },
+    "run-applescript": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-5.0.0.tgz",
+      "integrity": "sha512-XcT5rBksx1QdIhlFOCtgZkB99ZEouFZ1E2Kc2LHqNW13U3/74YGdkQRmThTwxy4QIyookibDKYZOPqX//6BlAg==",
+      "dev": true,
+      "requires": {
+        "execa": "^5.0.0"
+      },
+      "dependencies": {
+        "execa": {
+          "version": "5.1.1",
+          "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz",
+          "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==",
+          "dev": true,
+          "requires": {
+            "cross-spawn": "^7.0.3",
+            "get-stream": "^6.0.0",
+            "human-signals": "^2.1.0",
+            "is-stream": "^2.0.0",
+            "merge-stream": "^2.0.0",
+            "npm-run-path": "^4.0.1",
+            "onetime": "^5.1.2",
+            "signal-exit": "^3.0.3",
+            "strip-final-newline": "^2.0.0"
+          }
+        },
+        "human-signals": {
+          "version": "2.1.0",
+          "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz",
+          "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==",
+          "dev": true
+        },
+        "is-stream": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
+          "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==",
+          "dev": true
+        },
+        "mimic-fn": {
+          "version": "2.1.0",
+          "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
+          "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==",
+          "dev": true
+        },
+        "npm-run-path": {
+          "version": "4.0.1",
+          "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz",
+          "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==",
+          "dev": true,
+          "requires": {
+            "path-key": "^3.0.0"
+          }
+        },
+        "onetime": {
+          "version": "5.1.2",
+          "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz",
+          "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==",
+          "dev": true,
+          "requires": {
+            "mimic-fn": "^2.1.0"
+          }
+        },
+        "strip-final-newline": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz",
+          "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==",
+          "dev": true
+        }
+      }
+    },
     "run-parallel": {
       "version": "1.2.0",
       "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
@@ -10024,9 +10747,9 @@
       }
     },
     "sass": {
-      "version": "1.59.3",
-      "resolved": "https://registry.npmjs.org/sass/-/sass-1.59.3.tgz",
-      "integrity": "sha512-QCq98N3hX1jfTCoUAsF3eyGuXLsY7BCnCEg9qAact94Yc21npG2/mVOqoDvE0fCbWDqiM4WlcJQla0gWG2YlxQ==",
+      "version": "1.66.1",
+      "resolved": "https://registry.npmjs.org/sass/-/sass-1.66.1.tgz",
+      "integrity": "sha512-50c+zTsZOJVgFfTgwwEzkjA3/QACgdNsKueWPyAR0mRINIvLAStVQBbPg14iuqEQ74NPDbXzJARJ/O4SI1zftA==",
       "dev": true,
       "requires": {
         "chokidar": ">=3.0.0 <4.0.0",
@@ -10082,6 +10805,12 @@
         "object-inspect": "^1.9.0"
       }
     },
+    "signal-exit": {
+      "version": "3.0.7",
+      "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
+      "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
+      "dev": true
+    },
     "slash": {
       "version": "3.0.0",
       "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
@@ -10213,6 +10942,12 @@
       "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==",
       "dev": true
     },
+    "strip-final-newline": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz",
+      "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==",
+      "dev": true
+    },
     "strip-json-comments": {
       "version": "3.1.1",
       "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
@@ -10239,12 +10974,28 @@
       "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
       "dev": true
     },
+    "synckit": {
+      "version": "0.8.5",
+      "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.8.5.tgz",
+      "integrity": "sha512-L1dapNV6vu2s/4Sputv8xGsCdAVlb5nRDMFU/E27D44l5U6cw1g0dGd45uLc+OXjNMmF4ntiMdCimzcjFKQI8Q==",
+      "dev": true,
+      "requires": {
+        "@pkgr/utils": "^2.3.1",
+        "tslib": "^2.5.0"
+      }
+    },
     "text-table": {
       "version": "0.2.0",
       "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
       "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==",
       "dev": true
     },
+    "titleize": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/titleize/-/titleize-3.0.0.tgz",
+      "integrity": "sha512-KxVu8EYHDPBdUYdKZdKtU2aj2XfEx9AfjXxE/Aj0vT06w2icA09Vus1rh6eSu1y01akYg6BjIK/hxyLJINoMLQ==",
+      "dev": true
+    },
     "to-regex-range": {
       "version": "5.0.1",
       "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
@@ -10303,9 +11054,9 @@
       }
     },
     "typescript": {
-      "version": "4.9.5",
-      "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz",
-      "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==",
+      "version": "5.1.6",
+      "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.6.tgz",
+      "integrity": "sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==",
       "devOptional": true
     },
     "uglify-js": {
@@ -10333,6 +11084,12 @@
       "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==",
       "dev": true
     },
+    "untildify": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz",
+      "integrity": "sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==",
+      "dev": true
+    },
     "uri-js": {
       "version": "4.4.1",
       "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
@@ -10362,15 +11119,15 @@
       }
     },
     "vite": {
-      "version": "4.4.8",
-      "resolved": "https://registry.npmjs.org/vite/-/vite-4.4.8.tgz",
-      "integrity": "sha512-LONawOUUjxQridNWGQlNizfKH89qPigK36XhMI7COMGztz8KNY0JHim7/xDd71CZwGT4HtSRgI7Hy+RlhG0Gvg==",
+      "version": "4.4.9",
+      "resolved": "https://registry.npmjs.org/vite/-/vite-4.4.9.tgz",
+      "integrity": "sha512-2mbUn2LlUmNASWwSCNSJ/EG2HuSRTnVNaydp6vMCm5VIqJsjMfbIWtbH2kDuwUVW5mMUKKZvGPX/rqeqVvv1XA==",
       "dev": true,
       "requires": {
         "esbuild": "^0.18.10",
         "fsevents": "~2.3.2",
-        "postcss": "^8.4.26",
-        "rollup": "^3.25.2"
+        "postcss": "^8.4.27",
+        "rollup": "^3.27.1"
       }
     },
     "vue": {
@@ -10437,13 +11194,13 @@
       }
     },
     "vue-tsc": {
-      "version": "1.8.8",
-      "resolved": "https://registry.npmjs.org/vue-tsc/-/vue-tsc-1.8.8.tgz",
-      "integrity": "sha512-bSydNFQsF7AMvwWsRXD7cBIXaNs/KSjvzWLymq/UtKE36697sboX4EccSHFVxvgdBlI1frYPc/VMKJNB7DFeDQ==",
+      "version": "1.8.10",
+      "resolved": "https://registry.npmjs.org/vue-tsc/-/vue-tsc-1.8.10.tgz",
+      "integrity": "sha512-ptpTFFDoHQgkWJF7i5iERxooiQzOGtG1uKTfmAUuS3qPuSQGq+Ky/S8BFHhnFGwoOxq/PjmGN2QSZEfg1rtzQA==",
       "dev": true,
       "requires": {
-        "@vue/language-core": "1.8.8",
-        "@vue/typescript": "1.8.8",
+        "@vue/language-core": "1.8.10",
+        "@vue/typescript": "1.8.10",
         "semver": "^7.3.8"
       }
     },
diff --git a/package.json b/package.json
index 3666525993ddd2774d8d32fbb31084271cf6c776..4f1c1caaa7f9b2bc16f6fdee3ee370ae9dcd2c92 100644
--- a/package.json
+++ b/package.json
@@ -8,53 +8,54 @@
     "build-only": "vite build",
     "type-check": "vue-tsc --noEmit",
     "lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore",
-    "generate-s3-client": "openapi --input https://clowm-staging.bi.denbi.de/api/s3proxy-service/openapi.json --output src/client/s3proxy --client axios",
-    "generate-auth-client": "openapi --input https://clowm-staging.bi.denbi.de/api/auth-service/openapi.json --output src/client/auth --client axios",
-    "generate-workflow-client": "openapi --input https://clowm-staging.bi.denbi.de/api/workflow-service/openapi.json --output src/client/workflow --client axios"
+    "generate-s3-client": "openapi --input http://localhost:9999/api/s3proxy-service/openapi.json --output src/client/s3proxy --client axios",
+    "generate-auth-client": "openapi --input http://localhost:9999/api/auth-service/openapi.json --output src/client/auth --client axios",
+    "generate-workflow-client": "openapi --input http://localhost:9999/api/workflow-service/openapi.json --output src/client/workflow --client axios"
   },
   "dependencies": {
     "@aws-sdk/client-s3": "^3.379.1",
     "@aws-sdk/lib-storage": "^3.379.1",
     "@aws-sdk/s3-request-presigner": "^3.379.1",
-    "@fortawesome/fontawesome-free": "^6.4.0",
-    "@popperjs/core": "^2.11.8",
-    "ajv": "^8.12.0",
-    "bootstrap": "^5.2.3",
-    "chart.js": "^4.3.2",
-    "chartjs-plugin-zoom": "^2.0.1",
-    "dayjs": "^1.11.9",
-    "dompurify": "^3.0.5",
-    "filesize": "^10.0.8",
-    "pinia": "^2.1.6",
-    "semver": "^7.5.4",
-    "showdown": "^2.1.0",
-    "vue": "^3.3.4",
-    "vue-router": "^4.2.4",
-    "vue3-cookies": "^1.0.6"
+    "@fortawesome/fontawesome-free": "~6.4.2",
+    "@popperjs/core": "~2.11.8",
+    "ajv": "~8.12.0",
+    "bootstrap": "~5.3.1",
+    "chart.js": "~4.3.3",
+    "chartjs-plugin-zoom": "~2.0.1",
+    "dayjs": "~1.11.9",
+    "dompurify": "~3.0.5",
+    "filesize": "~10.0.12",
+    "pinia": "~2.1.6",
+    "semver": "~7.5.4",
+    "showdown": "~2.1.0",
+    "vue": "~3.3.4",
+    "vue-router": "~4.2.4",
+    "vue3-cookies": "~1.0.6"
   },
   "devDependencies": {
-    "@esbuild-plugins/node-globals-polyfill": "^0.2.3",
-    "@esbuild-plugins/node-modules-polyfill": "^0.2.2",
-    "@rushstack/eslint-patch": "^1.2.0",
-    "@types/bootstrap": "^5.2.6",
-    "@types/dompurify": "^3.0.2",
-    "@types/node": "^16.11.45",
-    "@types/semver": "^7.5.0",
-    "@types/showdown": "^2.0.1",
-    "@vitejs/plugin-vue": "^4.2.3",
-    "@vue/eslint-config-prettier": "^7.1.0",
-    "@vue/eslint-config-typescript": "^11.0.2",
-    "@vue/tsconfig": "^0.1.3",
-    "axios": "^1.4.0",
-    "eslint": "^8.46.0",
-    "eslint-plugin-vue": "^9.16.1",
-    "npm-run-all": "^4.1.5",
+    "@esbuild-plugins/node-globals-polyfill": "~0.2.3",
+    "@esbuild-plugins/node-modules-polyfill": "~0.2.2",
+    "@rushstack/eslint-patch": "~1.2.0",
+    "@tsconfig/node18": "^18.2.1",
+    "@types/bootstrap": "~5.2.6",
+    "@types/dompurify": "~3.0.2",
+    "@types/node": "^16.18.48",
+    "@types/semver": "~7.5.1",
+    "@types/showdown": "~2.0.1",
+    "@vitejs/plugin-vue": "~4.3.4",
+    "@vue/eslint-config-prettier": "~8.0.0",
+    "@vue/eslint-config-typescript": "~11.0.3",
+    "@vue/tsconfig": "~0.4.0",
+    "axios": "~1.5.0",
+    "eslint": "~8.48.0",
+    "eslint-plugin-vue": "~9.17.0",
+    "npm-run-all": "~4.1.5",
     "openapi-typescript-codegen": "^0.25.0",
-    "prettier": "^2.8.4",
-    "rollup-plugin-node-polyfills": "^0.2.1",
-    "sass": "^1.59.3",
-    "typescript": "~4.9.5",
-    "vite": "^4.2.3",
-    "vue-tsc": "^1.8.8"
+    "prettier": "~3.0.3",
+    "rollup-plugin-node-polyfills": "~0.2.1",
+    "sass": "~1.66.1",
+    "typescript": "~5.1.6",
+    "vite": "~4.4.9",
+    "vue-tsc": "~1.8.10"
   }
 }
diff --git a/src/assets/base.css b/src/assets/base.css
index 6b40a5ee14a7249f6935bc5967bedf02728976d5..c797992b2ac10ceba1399cfc918fc09cf7276cf5 100644
--- a/src/assets/base.css
+++ b/src/assets/base.css
@@ -47,8 +47,6 @@
 
 body {
   min-height: 100vh;
-  color: var(--color-text);
-  background: var(--color-background);
   transition: color 0.5s, background-color 0.5s;
   line-height: 1.6;
   font-family: Inter, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu,
diff --git a/src/client/auth/core/ApiError.ts b/src/client/auth/core/ApiError.ts
index 99d792996765118614adf6a232bc8766b95710e9..d6b8fcc3ad0b6b2bdf1aa4df97ec598e64995648 100644
--- a/src/client/auth/core/ApiError.ts
+++ b/src/client/auth/core/ApiError.ts
@@ -1,3 +1,4 @@
+/* generated using openapi-typescript-codegen -- do no edit */
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
diff --git a/src/client/auth/core/ApiRequestOptions.ts b/src/client/auth/core/ApiRequestOptions.ts
index c7b77538c5ce6aa48f141541392121fbcafc4821..c19adcc94dc5f015865368d6d64751de5c243aa3 100644
--- a/src/client/auth/core/ApiRequestOptions.ts
+++ b/src/client/auth/core/ApiRequestOptions.ts
@@ -1,3 +1,4 @@
+/* generated using openapi-typescript-codegen -- do no edit */
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
diff --git a/src/client/auth/core/ApiResult.ts b/src/client/auth/core/ApiResult.ts
index b095dc7708af2aa405a6dc7ad08f423334bc7f24..ad8fef2bc334c2aedf5c043896fd394229453ff0 100644
--- a/src/client/auth/core/ApiResult.ts
+++ b/src/client/auth/core/ApiResult.ts
@@ -1,3 +1,4 @@
+/* generated using openapi-typescript-codegen -- do no edit */
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
diff --git a/src/client/auth/core/CancelablePromise.ts b/src/client/auth/core/CancelablePromise.ts
index 26ad303915af161f170cc144a5e7684be3053778..55fef8517238d2dab7478598eefca72e657b9fb5 100644
--- a/src/client/auth/core/CancelablePromise.ts
+++ b/src/client/auth/core/CancelablePromise.ts
@@ -1,3 +1,4 @@
+/* generated using openapi-typescript-codegen -- do no edit */
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
@@ -22,15 +23,13 @@ export interface OnCancel {
 }
 
 export class CancelablePromise<T> implements Promise<T> {
-    readonly [Symbol.toStringTag]!: string;
-
-    private _isResolved: boolean;
-    private _isRejected: boolean;
-    private _isCancelled: boolean;
-    private readonly _cancelHandlers: (() => void)[];
-    private readonly _promise: Promise<T>;
-    private _resolve?: (value: T | PromiseLike<T>) => void;
-    private _reject?: (reason?: any) => void;
+    #isResolved: boolean;
+    #isRejected: boolean;
+    #isCancelled: boolean;
+    readonly #cancelHandlers: (() => void)[];
+    readonly #promise: Promise<T>;
+    #resolve?: (value: T | PromiseLike<T>) => void;
+    #reject?: (reason?: any) => void;
 
     constructor(
         executor: (
@@ -39,78 +38,82 @@ export class CancelablePromise<T> implements Promise<T> {
             onCancel: OnCancel
         ) => void
     ) {
-        this._isResolved = false;
-        this._isRejected = false;
-        this._isCancelled = false;
-        this._cancelHandlers = [];
-        this._promise = new Promise<T>((resolve, reject) => {
-            this._resolve = resolve;
-            this._reject = reject;
+        this.#isResolved = false;
+        this.#isRejected = false;
+        this.#isCancelled = false;
+        this.#cancelHandlers = [];
+        this.#promise = new Promise<T>((resolve, reject) => {
+            this.#resolve = resolve;
+            this.#reject = reject;
 
             const onResolve = (value: T | PromiseLike<T>): void => {
-                if (this._isResolved || this._isRejected || this._isCancelled) {
+                if (this.#isResolved || this.#isRejected || this.#isCancelled) {
                     return;
                 }
-                this._isResolved = true;
-                this._resolve?.(value);
+                this.#isResolved = true;
+                this.#resolve?.(value);
             };
 
             const onReject = (reason?: any): void => {
-                if (this._isResolved || this._isRejected || this._isCancelled) {
+                if (this.#isResolved || this.#isRejected || this.#isCancelled) {
                     return;
                 }
-                this._isRejected = true;
-                this._reject?.(reason);
+                this.#isRejected = true;
+                this.#reject?.(reason);
             };
 
             const onCancel = (cancelHandler: () => void): void => {
-                if (this._isResolved || this._isRejected || this._isCancelled) {
+                if (this.#isResolved || this.#isRejected || this.#isCancelled) {
                     return;
                 }
-                this._cancelHandlers.push(cancelHandler);
+                this.#cancelHandlers.push(cancelHandler);
             };
 
             Object.defineProperty(onCancel, 'isResolved', {
-                get: (): boolean => this._isResolved,
+                get: (): boolean => this.#isResolved,
             });
 
             Object.defineProperty(onCancel, 'isRejected', {
-                get: (): boolean => this._isRejected,
+                get: (): boolean => this.#isRejected,
             });
 
             Object.defineProperty(onCancel, 'isCancelled', {
-                get: (): boolean => this._isCancelled,
+                get: (): boolean => this.#isCancelled,
             });
 
             return executor(onResolve, onReject, onCancel as OnCancel);
         });
     }
 
+     get [Symbol.toStringTag]() {
+            return "Cancellable Promise";
+     }
+
     public then<TResult1 = T, TResult2 = never>(
         onFulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | null,
         onRejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null
     ): Promise<TResult1 | TResult2> {
-        return this._promise.then(onFulfilled, onRejected);
+        return this.#promise.then(onFulfilled, onRejected);
     }
 
     public catch<TResult = never>(
         onRejected?: ((reason: any) => TResult | PromiseLike<TResult>) | null
     ): Promise<T | TResult> {
-        return this._promise.catch(onRejected);
+        return this.#promise.catch(onRejected);
     }
 
     public finally(onFinally?: (() => void) | null): Promise<T> {
-        return this._promise.finally(onFinally);
+        return this.#promise.finally(onFinally);
     }
 
     public cancel(): void {
-        if (this._isResolved || this._isRejected || this._isCancelled) {
+        if (this.#isResolved || this.#isRejected || this.#isCancelled) {
             return;
         }
-        this._isCancelled = true;
-        if (this._cancelHandlers.length) {
+        this.#isCancelled = true;
+        if (this.#cancelHandlers.length) {
             try {
-                for (const cancelHandler of this._cancelHandlers) {
+                for (const cancelHandler of this.#cancelHandlers) {
                     cancelHandler();
                 }
             } catch (error) {
@@ -118,11 +121,11 @@ export class CancelablePromise<T> implements Promise<T> {
                 return;
             }
         }
-        this._cancelHandlers.length = 0;
-        this._reject?.(new CancelError('Request aborted'));
+        this.#cancelHandlers.length = 0;
+        this.#reject?.(new CancelError('Request aborted'));
     }
 
     public get isCancelled(): boolean {
-        return this._isCancelled;
+        return this.#isCancelled;
     }
 }
diff --git a/src/client/auth/core/OpenAPI.ts b/src/client/auth/core/OpenAPI.ts
index 27edd44a9d6ec44fc05b177eb541f5a518d7d285..9484fb91af3169d50a2a4fee46d01bed59b08907 100644
--- a/src/client/auth/core/OpenAPI.ts
+++ b/src/client/auth/core/OpenAPI.ts
@@ -1,3 +1,4 @@
+/* generated using openapi-typescript-codegen -- do no edit */
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
@@ -11,11 +12,11 @@ export type OpenAPIConfig = {
     VERSION: string;
     WITH_CREDENTIALS: boolean;
     CREDENTIALS: 'include' | 'omit' | 'same-origin';
-    TOKEN?: string | Resolver<string>;
-    USERNAME?: string | Resolver<string>;
-    PASSWORD?: string | Resolver<string>;
-    HEADERS?: Headers | Resolver<Headers>;
-    ENCODE_PATH?: (path: string) => string;
+    TOKEN?: string | Resolver<string> | undefined;
+    USERNAME?: string | Resolver<string> | undefined;
+    PASSWORD?: string | Resolver<string> | undefined;
+    HEADERS?: Headers | Resolver<Headers> | undefined;
+    ENCODE_PATH?: ((path: string) => string) | undefined;
 };
 
 export const OpenAPI: OpenAPIConfig = {
diff --git a/src/client/auth/core/request.ts b/src/client/auth/core/request.ts
index 2b5375ab44ec20863646a5debca820b42c2b7500..1142d43219797c94e09f35ba83a9f5441892ddd2 100644
--- a/src/client/auth/core/request.ts
+++ b/src/client/auth/core/request.ts
@@ -1,8 +1,9 @@
+/* generated using openapi-typescript-codegen -- do no edit */
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
 import axios from 'axios';
-import type { AxiosError, AxiosRequestConfig, AxiosResponse } from 'axios';
+import type { AxiosError, AxiosRequestConfig, AxiosResponse, AxiosInstance } from 'axios';
 import FormData from 'form-data';
 
 import { ApiError } from './ApiError';
@@ -12,19 +13,19 @@ import { CancelablePromise } from './CancelablePromise';
 import type { OnCancel } from './CancelablePromise';
 import type { OpenAPIConfig } from './OpenAPI';
 
-const isDefined = <T>(value: T | null | undefined): value is Exclude<T, null | undefined> => {
+export const isDefined = <T>(value: T | null | undefined): value is Exclude<T, null | undefined> => {
     return value !== undefined && value !== null;
 };
 
-const isString = (value: any): value is string => {
+export const isString = (value: any): value is string => {
     return typeof value === 'string';
 };
 
-const isStringWithValue = (value: any): value is string => {
+export const isStringWithValue = (value: any): value is string => {
     return isString(value) && value !== '';
 };
 
-const isBlob = (value: any): value is Blob => {
+export const isBlob = (value: any): value is Blob => {
     return (
         typeof value === 'object' &&
         typeof value.type === 'string' &&
@@ -37,15 +38,15 @@ const isBlob = (value: any): value is Blob => {
     );
 };
 
-const isFormData = (value: any): value is FormData => {
+export const isFormData = (value: any): value is FormData => {
     return value instanceof FormData;
 };
 
-const isSuccess = (status: number): boolean => {
+export const isSuccess = (status: number): boolean => {
     return status >= 200 && status < 300;
 };
 
-const base64 = (str: string): string => {
+export const base64 = (str: string): string => {
     try {
         return btoa(str);
     } catch (err) {
@@ -54,7 +55,7 @@ const base64 = (str: string): string => {
     }
 };
 
-const getQueryString = (params: Record<string, any>): string => {
+export const getQueryString = (params: Record<string, any>): string => {
     const qs: string[] = [];
 
     const append = (key: string, value: any) => {
@@ -107,7 +108,7 @@ const getUrl = (config: OpenAPIConfig, options: ApiRequestOptions): string => {
     return url;
 };
 
-const getFormData = (options: ApiRequestOptions): FormData | undefined => {
+export const getFormData = (options: ApiRequestOptions): FormData | undefined => {
     if (options.formData) {
         const formData = new FormData();
 
@@ -136,14 +137,14 @@ const getFormData = (options: ApiRequestOptions): FormData | undefined => {
 
 type Resolver<T> = (options: ApiRequestOptions) => Promise<T>;
 
-const resolve = async <T>(options: ApiRequestOptions, resolver?: T | Resolver<T>): Promise<T | undefined> => {
+export const resolve = async <T>(options: ApiRequestOptions, resolver?: T | Resolver<T>): Promise<T | undefined> => {
     if (typeof resolver === 'function') {
         return (resolver as Resolver<T>)(options);
     }
     return resolver;
 };
 
-const getHeaders = async (config: OpenAPIConfig, options: ApiRequestOptions, formData?: FormData): Promise<Record<string, string>> => {
+export const getHeaders = async (config: OpenAPIConfig, options: ApiRequestOptions, formData?: FormData): Promise<Record<string, string>> => {
     const token = await resolve(options, config.TOKEN);
     const username = await resolve(options, config.USERNAME);
     const password = await resolve(options, config.PASSWORD);
@@ -186,21 +187,22 @@ const getHeaders = async (config: OpenAPIConfig, options: ApiRequestOptions, for
     return headers;
 };
 
-const getRequestBody = (options: ApiRequestOptions): any => {
+export const getRequestBody = (options: ApiRequestOptions): any => {
     if (options.body) {
         return options.body;
     }
     return undefined;
 };
 
-const sendRequest = async <T>(
+export const sendRequest = async <T>(
     config: OpenAPIConfig,
     options: ApiRequestOptions,
     url: string,
     body: any,
     formData: FormData | undefined,
     headers: Record<string, string>,
-    onCancel: OnCancel
+    onCancel: OnCancel,
+    axiosClient: AxiosInstance
 ): Promise<AxiosResponse<T>> => {
     const source = axios.CancelToken.source();
 
@@ -216,7 +218,7 @@ const sendRequest = async <T>(
     onCancel(() => source.cancel('The user aborted a request.'));
 
     try {
-        return await axios.request(requestConfig);
+        return await axiosClient.request(requestConfig);
     } catch (error) {
         const axiosError = error as AxiosError<T>;
         if (axiosError.response) {
@@ -226,7 +228,7 @@ const sendRequest = async <T>(
     }
 };
 
-const getResponseHeader = (response: AxiosResponse<any>, responseHeader?: string): string | undefined => {
+export const getResponseHeader = (response: AxiosResponse<any>, responseHeader?: string): string | undefined => {
     if (responseHeader) {
         const content = response.headers[responseHeader];
         if (isString(content)) {
@@ -236,14 +238,14 @@ const getResponseHeader = (response: AxiosResponse<any>, responseHeader?: string
     return undefined;
 };
 
-const getResponseBody = (response: AxiosResponse<any>): any => {
+export const getResponseBody = (response: AxiosResponse<any>): any => {
     if (response.status !== 204) {
         return response.data;
     }
     return undefined;
 };
 
-const catchErrorCodes = (options: ApiRequestOptions, result: ApiResult): void => {
+export const catchErrorCodes = (options: ApiRequestOptions, result: ApiResult): void => {
     const errors: Record<number, string> = {
         400: 'Bad Request',
         401: 'Unauthorized',
@@ -261,7 +263,19 @@ const catchErrorCodes = (options: ApiRequestOptions, result: ApiResult): void =>
     }
 
     if (!result.ok) {
-        throw new ApiError(options, result, 'Generic Error');
+        const errorStatus = result.status ?? 'unknown';
+        const errorStatusText = result.statusText ?? 'unknown';
+        const errorBody = (() => {
+            try {
+                return JSON.stringify(result.body, null, 2);
+            } catch (e) {
+                return undefined;
+            }
+        })();
+
+        throw new ApiError(options, result,
+            `Generic Error: status: ${errorStatus}; status text: ${errorStatusText}; body: ${errorBody}`
+        );
     }
 };
 
@@ -269,10 +283,11 @@ const catchErrorCodes = (options: ApiRequestOptions, result: ApiResult): void =>
  * Request method
  * @param config The OpenAPI configuration object
  * @param options The request options from the service
+ * @param axiosClient The axios client instance to use
  * @returns CancelablePromise<T>
  * @throws ApiError
  */
-export const request = <T>(config: OpenAPIConfig, options: ApiRequestOptions): CancelablePromise<T> => {
+export const request = <T>(config: OpenAPIConfig, options: ApiRequestOptions, axiosClient: AxiosInstance = axios): CancelablePromise<T> => {
     return new CancelablePromise(async (resolve, reject, onCancel) => {
         try {
             const url = getUrl(config, options);
@@ -281,7 +296,7 @@ export const request = <T>(config: OpenAPIConfig, options: ApiRequestOptions): C
             const headers = await getHeaders(config, options, formData);
 
             if (!onCancel.isCancelled) {
-                const response = await sendRequest<T>(config, options, url, body, formData, headers, onCancel);
+                const response = await sendRequest<T>(config, options, url, body, formData, headers, onCancel, axiosClient);
                 const responseBody = getResponseBody(response);
                 const responseHeader = getResponseHeader(response, options.responseHeader);
 
diff --git a/src/client/auth/index.ts b/src/client/auth/index.ts
index b4f2ddad0aadc491252a1d254d3125358a304338..69ffa49395db64976f8d79f2dfbedcb90dc7fc5a 100644
--- a/src/client/auth/index.ts
+++ b/src/client/auth/index.ts
@@ -1,3 +1,4 @@
+/* generated using openapi-typescript-codegen -- do no edit */
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
diff --git a/src/client/auth/models/ErrorDetail.ts b/src/client/auth/models/ErrorDetail.ts
index b01e5aa28734c901620e98f8795e9528004d1513..343c8124f88bce59decea9cba6085126fe143db8 100644
--- a/src/client/auth/models/ErrorDetail.ts
+++ b/src/client/auth/models/ErrorDetail.ts
@@ -1,9 +1,12 @@
+/* generated using openapi-typescript-codegen -- do no edit */
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
 
 /**
+ *
  * Schema for a error due to a rejected request.
+ *
  */
 export type ErrorDetail = {
     /**
diff --git a/src/client/auth/models/HTTPValidationError.ts b/src/client/auth/models/HTTPValidationError.ts
index a8c21354123500873c7548194a36421eb8316c78..c0bcc87cf7f3222638466bc2e5753ca1accf01f7 100644
--- a/src/client/auth/models/HTTPValidationError.ts
+++ b/src/client/auth/models/HTTPValidationError.ts
@@ -1,3 +1,4 @@
+/* generated using openapi-typescript-codegen -- do no edit */
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
diff --git a/src/client/auth/models/RoleEnum.ts b/src/client/auth/models/RoleEnum.ts
index 4b9b63fd334827e077fd1d5155d4f2de6208ef07..c38121aa528efe32ad2db1868fe05ca559006f4b 100644
--- a/src/client/auth/models/RoleEnum.ts
+++ b/src/client/auth/models/RoleEnum.ts
@@ -1,3 +1,4 @@
+/* generated using openapi-typescript-codegen -- do no edit */
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
diff --git a/src/client/auth/models/User.ts b/src/client/auth/models/User.ts
index d33e8a91ed454283687c3d179c3f38af876679ac..fb73505018f43f56cff2810494177fe5da6c82b6 100644
--- a/src/client/auth/models/User.ts
+++ b/src/client/auth/models/User.ts
@@ -1,3 +1,4 @@
+/* generated using openapi-typescript-codegen -- do no edit */
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
@@ -5,7 +6,9 @@
 import type { RoleEnum } from './RoleEnum';
 
 /**
+ *
  * Schema for a user.
+ *
  */
 export type User = {
     /**
@@ -19,6 +22,6 @@ export type User = {
     /**
      * Roles of the user
      */
-    roles?: Array<RoleEnum>;
+    roles: (Array<RoleEnum> | null);
 };
 
diff --git a/src/client/auth/models/ValidationError.ts b/src/client/auth/models/ValidationError.ts
index 16dc86a6807d9ad1f7e9d06638d4bd002682dc42..18997ec72f4103731f38d915508522ba23ba8506 100644
--- a/src/client/auth/models/ValidationError.ts
+++ b/src/client/auth/models/ValidationError.ts
@@ -1,3 +1,4 @@
+/* generated using openapi-typescript-codegen -- do no edit */
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
diff --git a/src/client/auth/services/AuthService.ts b/src/client/auth/services/AuthService.ts
index 4e5156a38076f613a890cbfde32b7cdd14166b54..0472d3962a5f73dac257fe6de7e4c74cfe56f987 100644
--- a/src/client/auth/services/AuthService.ts
+++ b/src/client/auth/services/AuthService.ts
@@ -1,3 +1,4 @@
+/* generated using openapi-typescript-codegen -- do no edit */
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
diff --git a/src/client/auth/services/UserService.ts b/src/client/auth/services/UserService.ts
index 62cdb9335627acda756cf39fc80eecdaaa5de3e1..ae578426600f01927ec075bafe3d68b849366327 100644
--- a/src/client/auth/services/UserService.ts
+++ b/src/client/auth/services/UserService.ts
@@ -1,3 +1,4 @@
+/* generated using openapi-typescript-codegen -- do no edit */
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
@@ -34,15 +35,15 @@ export class UserService {
      * Return the users that have a specific substring in their name.
      * Permission 'user:read_any' required, except when 'name_substring' as only query parameter is set.
      * Then permission 'user:search' required.
-     * @param nameSubstring Filter users by a substring in their name. Permission 'search_user' required
+     * @param nameSubstring Filter users by a substring in their name. Permission 'search' required
      * @param filterRoles Filter users by their role. If multiple are selected, they are concatenating by an OR Expresssion. Permission 'read_any' required
      * @param includeRoles Flag whether to include the roles of the users in the response. If True, permission 'read_any' required.
      * @returns User Successful Response
      * @throws ApiError
      */
     public static userListUsers(
-        nameSubstring?: string,
-        filterRoles?: Array<RoleEnum>,
+        nameSubstring?: (string | null),
+        filterRoles?: (Array<RoleEnum> | null),
         includeRoles: boolean = false,
     ): CancelablePromise<Array<User>> {
         return __request(OpenAPI, {
@@ -65,6 +66,7 @@ export class UserService {
     /**
      * Get a user by its uid
      * Return the user with the specific uid. A user can only view himself.
+     * Permission 'user:read' required
      * @param uid UID of a user
      * @param includeRoles Flag whether to include the roles of the users in the response. If True, permission 'read_any' required.
      * @returns User Successful Response
diff --git a/src/client/s3proxy/core/ApiError.ts b/src/client/s3proxy/core/ApiError.ts
index 99d792996765118614adf6a232bc8766b95710e9..d6b8fcc3ad0b6b2bdf1aa4df97ec598e64995648 100644
--- a/src/client/s3proxy/core/ApiError.ts
+++ b/src/client/s3proxy/core/ApiError.ts
@@ -1,3 +1,4 @@
+/* generated using openapi-typescript-codegen -- do no edit */
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
diff --git a/src/client/s3proxy/core/ApiRequestOptions.ts b/src/client/s3proxy/core/ApiRequestOptions.ts
index c7b77538c5ce6aa48f141541392121fbcafc4821..c19adcc94dc5f015865368d6d64751de5c243aa3 100644
--- a/src/client/s3proxy/core/ApiRequestOptions.ts
+++ b/src/client/s3proxy/core/ApiRequestOptions.ts
@@ -1,3 +1,4 @@
+/* generated using openapi-typescript-codegen -- do no edit */
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
diff --git a/src/client/s3proxy/core/ApiResult.ts b/src/client/s3proxy/core/ApiResult.ts
index b095dc7708af2aa405a6dc7ad08f423334bc7f24..ad8fef2bc334c2aedf5c043896fd394229453ff0 100644
--- a/src/client/s3proxy/core/ApiResult.ts
+++ b/src/client/s3proxy/core/ApiResult.ts
@@ -1,3 +1,4 @@
+/* generated using openapi-typescript-codegen -- do no edit */
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
diff --git a/src/client/s3proxy/core/CancelablePromise.ts b/src/client/s3proxy/core/CancelablePromise.ts
index 26ad303915af161f170cc144a5e7684be3053778..55fef8517238d2dab7478598eefca72e657b9fb5 100644
--- a/src/client/s3proxy/core/CancelablePromise.ts
+++ b/src/client/s3proxy/core/CancelablePromise.ts
@@ -1,3 +1,4 @@
+/* generated using openapi-typescript-codegen -- do no edit */
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
@@ -22,15 +23,13 @@ export interface OnCancel {
 }
 
 export class CancelablePromise<T> implements Promise<T> {
-    readonly [Symbol.toStringTag]!: string;
-
-    private _isResolved: boolean;
-    private _isRejected: boolean;
-    private _isCancelled: boolean;
-    private readonly _cancelHandlers: (() => void)[];
-    private readonly _promise: Promise<T>;
-    private _resolve?: (value: T | PromiseLike<T>) => void;
-    private _reject?: (reason?: any) => void;
+    #isResolved: boolean;
+    #isRejected: boolean;
+    #isCancelled: boolean;
+    readonly #cancelHandlers: (() => void)[];
+    readonly #promise: Promise<T>;
+    #resolve?: (value: T | PromiseLike<T>) => void;
+    #reject?: (reason?: any) => void;
 
     constructor(
         executor: (
@@ -39,78 +38,82 @@ export class CancelablePromise<T> implements Promise<T> {
             onCancel: OnCancel
         ) => void
     ) {
-        this._isResolved = false;
-        this._isRejected = false;
-        this._isCancelled = false;
-        this._cancelHandlers = [];
-        this._promise = new Promise<T>((resolve, reject) => {
-            this._resolve = resolve;
-            this._reject = reject;
+        this.#isResolved = false;
+        this.#isRejected = false;
+        this.#isCancelled = false;
+        this.#cancelHandlers = [];
+        this.#promise = new Promise<T>((resolve, reject) => {
+            this.#resolve = resolve;
+            this.#reject = reject;
 
             const onResolve = (value: T | PromiseLike<T>): void => {
-                if (this._isResolved || this._isRejected || this._isCancelled) {
+                if (this.#isResolved || this.#isRejected || this.#isCancelled) {
                     return;
                 }
-                this._isResolved = true;
-                this._resolve?.(value);
+                this.#isResolved = true;
+                this.#resolve?.(value);
             };
 
             const onReject = (reason?: any): void => {
-                if (this._isResolved || this._isRejected || this._isCancelled) {
+                if (this.#isResolved || this.#isRejected || this.#isCancelled) {
                     return;
                 }
-                this._isRejected = true;
-                this._reject?.(reason);
+                this.#isRejected = true;
+                this.#reject?.(reason);
             };
 
             const onCancel = (cancelHandler: () => void): void => {
-                if (this._isResolved || this._isRejected || this._isCancelled) {
+                if (this.#isResolved || this.#isRejected || this.#isCancelled) {
                     return;
                 }
-                this._cancelHandlers.push(cancelHandler);
+                this.#cancelHandlers.push(cancelHandler);
             };
 
             Object.defineProperty(onCancel, 'isResolved', {
-                get: (): boolean => this._isResolved,
+                get: (): boolean => this.#isResolved,
             });
 
             Object.defineProperty(onCancel, 'isRejected', {
-                get: (): boolean => this._isRejected,
+                get: (): boolean => this.#isRejected,
             });
 
             Object.defineProperty(onCancel, 'isCancelled', {
-                get: (): boolean => this._isCancelled,
+                get: (): boolean => this.#isCancelled,
             });
 
             return executor(onResolve, onReject, onCancel as OnCancel);
         });
     }
 
+     get [Symbol.toStringTag]() {
+            return "Cancellable Promise";
+     }
+
     public then<TResult1 = T, TResult2 = never>(
         onFulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | null,
         onRejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null
     ): Promise<TResult1 | TResult2> {
-        return this._promise.then(onFulfilled, onRejected);
+        return this.#promise.then(onFulfilled, onRejected);
     }
 
     public catch<TResult = never>(
         onRejected?: ((reason: any) => TResult | PromiseLike<TResult>) | null
     ): Promise<T | TResult> {
-        return this._promise.catch(onRejected);
+        return this.#promise.catch(onRejected);
     }
 
     public finally(onFinally?: (() => void) | null): Promise<T> {
-        return this._promise.finally(onFinally);
+        return this.#promise.finally(onFinally);
     }
 
     public cancel(): void {
-        if (this._isResolved || this._isRejected || this._isCancelled) {
+        if (this.#isResolved || this.#isRejected || this.#isCancelled) {
             return;
         }
-        this._isCancelled = true;
-        if (this._cancelHandlers.length) {
+        this.#isCancelled = true;
+        if (this.#cancelHandlers.length) {
             try {
-                for (const cancelHandler of this._cancelHandlers) {
+                for (const cancelHandler of this.#cancelHandlers) {
                     cancelHandler();
                 }
             } catch (error) {
@@ -118,11 +121,11 @@ export class CancelablePromise<T> implements Promise<T> {
                 return;
             }
         }
-        this._cancelHandlers.length = 0;
-        this._reject?.(new CancelError('Request aborted'));
+        this.#cancelHandlers.length = 0;
+        this.#reject?.(new CancelError('Request aborted'));
     }
 
     public get isCancelled(): boolean {
-        return this._isCancelled;
+        return this.#isCancelled;
     }
 }
diff --git a/src/client/s3proxy/core/OpenAPI.ts b/src/client/s3proxy/core/OpenAPI.ts
index 58ca8b0348f7e6038e827d3721a8fd5efe592205..f18fb9aeed82d0fd92b854566f9b61b5d519e1ca 100644
--- a/src/client/s3proxy/core/OpenAPI.ts
+++ b/src/client/s3proxy/core/OpenAPI.ts
@@ -1,3 +1,4 @@
+/* generated using openapi-typescript-codegen -- do no edit */
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
@@ -11,11 +12,11 @@ export type OpenAPIConfig = {
     VERSION: string;
     WITH_CREDENTIALS: boolean;
     CREDENTIALS: 'include' | 'omit' | 'same-origin';
-    TOKEN?: string | Resolver<string>;
-    USERNAME?: string | Resolver<string>;
-    PASSWORD?: string | Resolver<string>;
-    HEADERS?: Headers | Resolver<Headers>;
-    ENCODE_PATH?: (path: string) => string;
+    TOKEN?: string | Resolver<string> | undefined;
+    USERNAME?: string | Resolver<string> | undefined;
+    PASSWORD?: string | Resolver<string> | undefined;
+    HEADERS?: Headers | Resolver<Headers> | undefined;
+    ENCODE_PATH?: ((path: string) => string) | undefined;
 };
 
 export const OpenAPI: OpenAPIConfig = {
diff --git a/src/client/s3proxy/core/request.ts b/src/client/s3proxy/core/request.ts
index 2b5375ab44ec20863646a5debca820b42c2b7500..1142d43219797c94e09f35ba83a9f5441892ddd2 100644
--- a/src/client/s3proxy/core/request.ts
+++ b/src/client/s3proxy/core/request.ts
@@ -1,8 +1,9 @@
+/* generated using openapi-typescript-codegen -- do no edit */
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
 import axios from 'axios';
-import type { AxiosError, AxiosRequestConfig, AxiosResponse } from 'axios';
+import type { AxiosError, AxiosRequestConfig, AxiosResponse, AxiosInstance } from 'axios';
 import FormData from 'form-data';
 
 import { ApiError } from './ApiError';
@@ -12,19 +13,19 @@ import { CancelablePromise } from './CancelablePromise';
 import type { OnCancel } from './CancelablePromise';
 import type { OpenAPIConfig } from './OpenAPI';
 
-const isDefined = <T>(value: T | null | undefined): value is Exclude<T, null | undefined> => {
+export const isDefined = <T>(value: T | null | undefined): value is Exclude<T, null | undefined> => {
     return value !== undefined && value !== null;
 };
 
-const isString = (value: any): value is string => {
+export const isString = (value: any): value is string => {
     return typeof value === 'string';
 };
 
-const isStringWithValue = (value: any): value is string => {
+export const isStringWithValue = (value: any): value is string => {
     return isString(value) && value !== '';
 };
 
-const isBlob = (value: any): value is Blob => {
+export const isBlob = (value: any): value is Blob => {
     return (
         typeof value === 'object' &&
         typeof value.type === 'string' &&
@@ -37,15 +38,15 @@ const isBlob = (value: any): value is Blob => {
     );
 };
 
-const isFormData = (value: any): value is FormData => {
+export const isFormData = (value: any): value is FormData => {
     return value instanceof FormData;
 };
 
-const isSuccess = (status: number): boolean => {
+export const isSuccess = (status: number): boolean => {
     return status >= 200 && status < 300;
 };
 
-const base64 = (str: string): string => {
+export const base64 = (str: string): string => {
     try {
         return btoa(str);
     } catch (err) {
@@ -54,7 +55,7 @@ const base64 = (str: string): string => {
     }
 };
 
-const getQueryString = (params: Record<string, any>): string => {
+export const getQueryString = (params: Record<string, any>): string => {
     const qs: string[] = [];
 
     const append = (key: string, value: any) => {
@@ -107,7 +108,7 @@ const getUrl = (config: OpenAPIConfig, options: ApiRequestOptions): string => {
     return url;
 };
 
-const getFormData = (options: ApiRequestOptions): FormData | undefined => {
+export const getFormData = (options: ApiRequestOptions): FormData | undefined => {
     if (options.formData) {
         const formData = new FormData();
 
@@ -136,14 +137,14 @@ const getFormData = (options: ApiRequestOptions): FormData | undefined => {
 
 type Resolver<T> = (options: ApiRequestOptions) => Promise<T>;
 
-const resolve = async <T>(options: ApiRequestOptions, resolver?: T | Resolver<T>): Promise<T | undefined> => {
+export const resolve = async <T>(options: ApiRequestOptions, resolver?: T | Resolver<T>): Promise<T | undefined> => {
     if (typeof resolver === 'function') {
         return (resolver as Resolver<T>)(options);
     }
     return resolver;
 };
 
-const getHeaders = async (config: OpenAPIConfig, options: ApiRequestOptions, formData?: FormData): Promise<Record<string, string>> => {
+export const getHeaders = async (config: OpenAPIConfig, options: ApiRequestOptions, formData?: FormData): Promise<Record<string, string>> => {
     const token = await resolve(options, config.TOKEN);
     const username = await resolve(options, config.USERNAME);
     const password = await resolve(options, config.PASSWORD);
@@ -186,21 +187,22 @@ const getHeaders = async (config: OpenAPIConfig, options: ApiRequestOptions, for
     return headers;
 };
 
-const getRequestBody = (options: ApiRequestOptions): any => {
+export const getRequestBody = (options: ApiRequestOptions): any => {
     if (options.body) {
         return options.body;
     }
     return undefined;
 };
 
-const sendRequest = async <T>(
+export const sendRequest = async <T>(
     config: OpenAPIConfig,
     options: ApiRequestOptions,
     url: string,
     body: any,
     formData: FormData | undefined,
     headers: Record<string, string>,
-    onCancel: OnCancel
+    onCancel: OnCancel,
+    axiosClient: AxiosInstance
 ): Promise<AxiosResponse<T>> => {
     const source = axios.CancelToken.source();
 
@@ -216,7 +218,7 @@ const sendRequest = async <T>(
     onCancel(() => source.cancel('The user aborted a request.'));
 
     try {
-        return await axios.request(requestConfig);
+        return await axiosClient.request(requestConfig);
     } catch (error) {
         const axiosError = error as AxiosError<T>;
         if (axiosError.response) {
@@ -226,7 +228,7 @@ const sendRequest = async <T>(
     }
 };
 
-const getResponseHeader = (response: AxiosResponse<any>, responseHeader?: string): string | undefined => {
+export const getResponseHeader = (response: AxiosResponse<any>, responseHeader?: string): string | undefined => {
     if (responseHeader) {
         const content = response.headers[responseHeader];
         if (isString(content)) {
@@ -236,14 +238,14 @@ const getResponseHeader = (response: AxiosResponse<any>, responseHeader?: string
     return undefined;
 };
 
-const getResponseBody = (response: AxiosResponse<any>): any => {
+export const getResponseBody = (response: AxiosResponse<any>): any => {
     if (response.status !== 204) {
         return response.data;
     }
     return undefined;
 };
 
-const catchErrorCodes = (options: ApiRequestOptions, result: ApiResult): void => {
+export const catchErrorCodes = (options: ApiRequestOptions, result: ApiResult): void => {
     const errors: Record<number, string> = {
         400: 'Bad Request',
         401: 'Unauthorized',
@@ -261,7 +263,19 @@ const catchErrorCodes = (options: ApiRequestOptions, result: ApiResult): void =>
     }
 
     if (!result.ok) {
-        throw new ApiError(options, result, 'Generic Error');
+        const errorStatus = result.status ?? 'unknown';
+        const errorStatusText = result.statusText ?? 'unknown';
+        const errorBody = (() => {
+            try {
+                return JSON.stringify(result.body, null, 2);
+            } catch (e) {
+                return undefined;
+            }
+        })();
+
+        throw new ApiError(options, result,
+            `Generic Error: status: ${errorStatus}; status text: ${errorStatusText}; body: ${errorBody}`
+        );
     }
 };
 
@@ -269,10 +283,11 @@ const catchErrorCodes = (options: ApiRequestOptions, result: ApiResult): void =>
  * Request method
  * @param config The OpenAPI configuration object
  * @param options The request options from the service
+ * @param axiosClient The axios client instance to use
  * @returns CancelablePromise<T>
  * @throws ApiError
  */
-export const request = <T>(config: OpenAPIConfig, options: ApiRequestOptions): CancelablePromise<T> => {
+export const request = <T>(config: OpenAPIConfig, options: ApiRequestOptions, axiosClient: AxiosInstance = axios): CancelablePromise<T> => {
     return new CancelablePromise(async (resolve, reject, onCancel) => {
         try {
             const url = getUrl(config, options);
@@ -281,7 +296,7 @@ export const request = <T>(config: OpenAPIConfig, options: ApiRequestOptions): C
             const headers = await getHeaders(config, options, formData);
 
             if (!onCancel.isCancelled) {
-                const response = await sendRequest<T>(config, options, url, body, formData, headers, onCancel);
+                const response = await sendRequest<T>(config, options, url, body, formData, headers, onCancel, axiosClient);
                 const responseBody = getResponseBody(response);
                 const responseHeader = getResponseHeader(response, options.responseHeader);
 
diff --git a/src/client/s3proxy/index.ts b/src/client/s3proxy/index.ts
index 3006a77d6c0beb2084eef7aedd5d7ddca05702e5..0748f5b918bfb1cd83e3fe4c546af20bca1caeff 100644
--- a/src/client/s3proxy/index.ts
+++ b/src/client/s3proxy/index.ts
@@ -1,3 +1,4 @@
+/* generated using openapi-typescript-codegen -- do no edit */
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
diff --git a/src/client/s3proxy/models/BucketIn.ts b/src/client/s3proxy/models/BucketIn.ts
index 530031e6c9db2eed482ac3b2a05f512112fdcb3e..c088c1be4e4944e95f6bcc400ba85ea03c59ad91 100644
--- a/src/client/s3proxy/models/BucketIn.ts
+++ b/src/client/s3proxy/models/BucketIn.ts
@@ -1,9 +1,12 @@
+/* generated using openapi-typescript-codegen -- do no edit */
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
 
 /**
+ *
  * Schema for creating a new bucket.
+ *
  */
 export type BucketIn = {
     /**
diff --git a/src/client/s3proxy/models/BucketOut.ts b/src/client/s3proxy/models/BucketOut.ts
index 5fd172a904ae83eab5401c23b30ed9fef8ac18a3..20e3d57428c53597a75151beb4f0b739f038fed3 100644
--- a/src/client/s3proxy/models/BucketOut.ts
+++ b/src/client/s3proxy/models/BucketOut.ts
@@ -1,3 +1,4 @@
+/* generated using openapi-typescript-codegen -- do no edit */
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
@@ -5,7 +6,9 @@
 import type { Constraint } from './Constraint';
 
 /**
+ *
  * Schema for answering a request with a bucket.
+ *
  */
 export type BucketOut = {
     /**
@@ -17,9 +20,9 @@ export type BucketOut = {
      */
     description: string;
     /**
-     * Time when the bucket was created
+     * Time when the bucket was created as UNIX timestamp
      */
-    created_at: string;
+    created_at: number;
     /**
      * UID of the owner
      */
@@ -35,6 +38,6 @@ export type BucketOut = {
     /**
      * Constraint for the owner of the bucket
      */
-    owner_constraint?: Constraint;
+    owner_constraint: (Constraint | null);
 };
 
diff --git a/src/client/s3proxy/models/BucketPermissionIn.ts b/src/client/s3proxy/models/BucketPermissionIn.ts
index 315a6a5f8ebc4820470a4ebcd5f7503d0b52065a..06bd223c69ce58189c1699343b1018187a8942c1 100644
--- a/src/client/s3proxy/models/BucketPermissionIn.ts
+++ b/src/client/s3proxy/models/BucketPermissionIn.ts
@@ -1,25 +1,23 @@
+/* generated using openapi-typescript-codegen -- do no edit */
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
 
 import type { Permission } from './Permission';
 
-/**
- * Schema for the parameters of a bucket permission.
- */
 export type BucketPermissionIn = {
     /**
-     * Start date of permission
+     * Start date of permission as UNIX timestamp
      */
-    from_timestamp?: string;
+    from_timestamp?: (number | null);
     /**
-     * End date of permission
+     * End date of permission as UNIX timestamp
      */
-    to_timestamp?: string;
+    to_timestamp?: (number | null);
     /**
      * Prefix of subfolder
      */
-    file_prefix?: string;
+    file_prefix?: (string | null);
     /**
      * Permission
      */
diff --git a/src/client/s3proxy/models/BucketPermissionOut.ts b/src/client/s3proxy/models/BucketPermissionOut.ts
index 86c6b26bc3b94e97b75ad219affb1dfe38509b3b..dfa89530c878b0dbff5f9a62e0e1a9034f210729 100644
--- a/src/client/s3proxy/models/BucketPermissionOut.ts
+++ b/src/client/s3proxy/models/BucketPermissionOut.ts
@@ -1,3 +1,4 @@
+/* generated using openapi-typescript-codegen -- do no edit */
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
@@ -5,25 +6,27 @@
 import type { Permission } from './Permission';
 
 /**
+ *
  * Schema for the bucket permissions.
+ *
  */
 export type BucketPermissionOut = {
     /**
-     * Start date of permission
+     * Start date of permission as UNIX timestamp
      */
-    from_timestamp?: string;
+    from_timestamp: (number | null);
     /**
-     * End date of permission
+     * End date of permission as UNIX timestamp
      */
-    to_timestamp?: string;
+    to_timestamp: (number | null);
     /**
      * Prefix of subfolder
      */
-    file_prefix?: string;
+    file_prefix: (string | null);
     /**
      * Permission
      */
-    permission?: (Permission | string);
+    permission: (Permission | string);
     /**
      * UID of the grantee
      */
diff --git a/src/client/s3proxy/models/BucketPermissionParameters.ts b/src/client/s3proxy/models/BucketPermissionParameters.ts
index 3317e6a70362dfaf2f390490ad641dac0c81ec64..6eca4a9543e251adc45c1a109124e22024e6f585 100644
--- a/src/client/s3proxy/models/BucketPermissionParameters.ts
+++ b/src/client/s3proxy/models/BucketPermissionParameters.ts
@@ -1,3 +1,4 @@
+/* generated using openapi-typescript-codegen -- do no edit */
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
@@ -5,21 +6,23 @@
 import type { Permission } from './Permission';
 
 /**
+ *
  * Schema for the parameters of a bucket permission.
+ *
  */
 export type BucketPermissionParameters = {
     /**
-     * Start date of permission
+     * Start date of permission as UNIX timestamp
      */
-    from_timestamp?: string;
+    from_timestamp?: (number | null);
     /**
-     * End date of permission
+     * End date of permission as UNIX timestamp
      */
-    to_timestamp?: string;
+    to_timestamp?: (number | null);
     /**
      * Prefix of subfolder
      */
-    file_prefix?: string;
+    file_prefix?: (string | null);
     /**
      * Permission
      */
diff --git a/src/client/s3proxy/models/BucketType.ts b/src/client/s3proxy/models/BucketType.ts
index ba0c539062b0cda4b04349e01c229f3bb4c1c0dc..b096c70b0b7234bc57dd1ecc2223bd41aa3e0d08 100644
--- a/src/client/s3proxy/models/BucketType.ts
+++ b/src/client/s3proxy/models/BucketType.ts
@@ -1,3 +1,4 @@
+/* generated using openapi-typescript-codegen -- do no edit */
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
diff --git a/src/client/s3proxy/models/Constraint.ts b/src/client/s3proxy/models/Constraint.ts
index 2eb7b569f9ee51ac37791185db5cacc6949af245..afa995a983b5e08b66d55b8dfa4243553351d3a7 100644
--- a/src/client/s3proxy/models/Constraint.ts
+++ b/src/client/s3proxy/models/Constraint.ts
@@ -1,3 +1,4 @@
+/* generated using openapi-typescript-codegen -- do no edit */
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
diff --git a/src/client/s3proxy/models/ErrorDetail.ts b/src/client/s3proxy/models/ErrorDetail.ts
index b01e5aa28734c901620e98f8795e9528004d1513..343c8124f88bce59decea9cba6085126fe143db8 100644
--- a/src/client/s3proxy/models/ErrorDetail.ts
+++ b/src/client/s3proxy/models/ErrorDetail.ts
@@ -1,9 +1,12 @@
+/* generated using openapi-typescript-codegen -- do no edit */
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
 
 /**
+ *
  * Schema for a error due to a rejected request.
+ *
  */
 export type ErrorDetail = {
     /**
diff --git a/src/client/s3proxy/models/HTTPValidationError.ts b/src/client/s3proxy/models/HTTPValidationError.ts
index a8c21354123500873c7548194a36421eb8316c78..c0bcc87cf7f3222638466bc2e5753ca1accf01f7 100644
--- a/src/client/s3proxy/models/HTTPValidationError.ts
+++ b/src/client/s3proxy/models/HTTPValidationError.ts
@@ -1,3 +1,4 @@
+/* generated using openapi-typescript-codegen -- do no edit */
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
diff --git a/src/client/s3proxy/models/Permission.ts b/src/client/s3proxy/models/Permission.ts
index cacede746c4a5a681c274299b195e373c5616d43..00592a496a01e178e7192ba3bd76c51b29a3a138 100644
--- a/src/client/s3proxy/models/Permission.ts
+++ b/src/client/s3proxy/models/Permission.ts
@@ -1,3 +1,4 @@
+/* generated using openapi-typescript-codegen -- do no edit */
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
diff --git a/src/client/s3proxy/models/PermissionStatus.ts b/src/client/s3proxy/models/PermissionStatus.ts
index fb8a55251c5b311395d815d829faff37a73bcac1..d51e0928ec89e662e033c833ceacd5bd6c66bc74 100644
--- a/src/client/s3proxy/models/PermissionStatus.ts
+++ b/src/client/s3proxy/models/PermissionStatus.ts
@@ -1,3 +1,4 @@
+/* generated using openapi-typescript-codegen -- do no edit */
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
diff --git a/src/client/s3proxy/models/S3Key.ts b/src/client/s3proxy/models/S3Key.ts
index 5a1aa255ea54281fb45ae5a32d09d459ef1bac29..bcdec338b5948f9b78c53f7721dd42f8ca12bcdd 100644
--- a/src/client/s3proxy/models/S3Key.ts
+++ b/src/client/s3proxy/models/S3Key.ts
@@ -1,9 +1,12 @@
+/* generated using openapi-typescript-codegen -- do no edit */
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
 
 /**
+ *
  * Schema for a S3 key associated with a user.
+ *
  */
 export type S3Key = {
     /**
diff --git a/src/client/s3proxy/models/S3ObjectMetaInformation.ts b/src/client/s3proxy/models/S3ObjectMetaInformation.ts
index ed92a733014053f1c91d4ac7f4067271fb8b6904..8d53cecc418f9ebdadc7c43f6eaea19da292a0f0 100644
--- a/src/client/s3proxy/models/S3ObjectMetaInformation.ts
+++ b/src/client/s3proxy/models/S3ObjectMetaInformation.ts
@@ -1,9 +1,12 @@
+/* generated using openapi-typescript-codegen -- do no edit */
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
 
 /**
+ *
  * Schema for the meta-information about a S3 object.
+ *
  */
 export type S3ObjectMetaInformation = {
     /**
diff --git a/src/client/s3proxy/models/ValidationError.ts b/src/client/s3proxy/models/ValidationError.ts
index 16dc86a6807d9ad1f7e9d06638d4bd002682dc42..18997ec72f4103731f38d915508522ba23ba8506 100644
--- a/src/client/s3proxy/models/ValidationError.ts
+++ b/src/client/s3proxy/models/ValidationError.ts
@@ -1,3 +1,4 @@
+/* generated using openapi-typescript-codegen -- do no edit */
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
diff --git a/src/client/s3proxy/services/BucketPermissionService.ts b/src/client/s3proxy/services/BucketPermissionService.ts
index ca7cba59dbb8af1dbb3f88bd9b0e3d125f2a148c..365cf2ee156716b11099883696bbb7d52bda3146 100644
--- a/src/client/s3proxy/services/BucketPermissionService.ts
+++ b/src/client/s3proxy/services/BucketPermissionService.ts
@@ -1,3 +1,4 @@
+/* generated using openapi-typescript-codegen -- do no edit */
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
@@ -47,31 +48,29 @@ export class BucketPermissionService {
     }
 
     /**
-     * Update a bucket permission
-     * Update a permission for a bucket and user.
+     * Delete a bucket permission
+     * Delete the bucket permissions for the specific combination of bucket and user.
      *
-     * Permission "bucket_permission:read" required if current user is the target the bucket permission,
-     * otherwise "bucket_permission:update" required.
-     * @param uid UID of a user
+     * The owner of the bucket and the grantee of the permission can delete it.
+     *
+     * Permission "bucket_permission:delete" required if current user is the target or owner of the bucket permission,
+     * otherwise "bucket_permission:delete_any" required.
      * @param bucketName Name of bucket
-     * @param requestBody
-     * @returns BucketPermissionOut Successful Response
+     * @param uid UID of a user
+     * @returns void
      * @throws ApiError
      */
-    public static bucketPermissionUpdatePermission(
-        uid: string,
+    public static bucketPermissionDeletePermissionForBucket(
         bucketName: string,
-        requestBody: BucketPermissionParameters,
-    ): CancelablePromise<BucketPermissionOut> {
+        uid: string,
+    ): CancelablePromise<void> {
         return __request(OpenAPI, {
-            method: 'PUT',
+            method: 'DELETE',
             url: '/permissions/bucket/{bucket_name}/user/{uid}',
             path: {
-                'uid': uid,
                 'bucket_name': bucketName,
+                'uid': uid,
             },
-            body: requestBody,
-            mediaType: 'application/json',
             errors: {
                 400: `Error decoding JWT Token`,
                 403: `Not authenticated`,
@@ -82,29 +81,31 @@ export class BucketPermissionService {
     }
 
     /**
-     * Delete a bucket permission
-     * Delete the bucket permissions for the specific combination of bucket and user.
-     *
-     * The owner of the bucket and the grantee of the permission can delete it.
+     * Update a bucket permission
+     * Update a permission for a bucket and user.
      *
-     * Permission "bucket_permission:delete" required if current user is the target or owner of the bucket permission,
-     * otherwise "bucket_permission:delete_any" required.
+     * Permission "bucket_permission:read" required if current user is the target the bucket permission,
+     * otherwise "bucket_permission:update" required.
      * @param bucketName Name of bucket
      * @param uid UID of a user
-     * @returns void
+     * @param requestBody
+     * @returns BucketPermissionOut Successful Response
      * @throws ApiError
      */
-    public static bucketPermissionDeletePermissionForBucket(
+    public static bucketPermissionUpdatePermission(
         bucketName: string,
         uid: string,
-    ): CancelablePromise<void> {
+        requestBody: BucketPermissionParameters,
+    ): CancelablePromise<BucketPermissionOut> {
         return __request(OpenAPI, {
-            method: 'DELETE',
+            method: 'PUT',
             url: '/permissions/bucket/{bucket_name}/user/{uid}',
             path: {
                 'bucket_name': bucketName,
                 'uid': uid,
             },
+            body: requestBody,
+            mediaType: 'application/json',
             errors: {
                 400: `Error decoding JWT Token`,
                 403: `Not authenticated`,
@@ -128,8 +129,8 @@ export class BucketPermissionService {
      */
     public static bucketPermissionListPermissionsPerBucket(
         bucketName: string,
-        permissionType?: Array<Permission>,
-        permissionStatus?: PermissionStatus,
+        permissionType?: (Array<Permission> | null),
+        permissionStatus?: (PermissionStatus | null),
     ): CancelablePromise<Array<BucketPermissionOut>> {
         return __request(OpenAPI, {
             method: 'GET',
@@ -164,8 +165,8 @@ export class BucketPermissionService {
      */
     public static bucketPermissionListPermissionsPerUser(
         uid: string,
-        permissionType?: Array<Permission>,
-        permissionStatus?: PermissionStatus,
+        permissionType?: (Array<Permission> | null),
+        permissionStatus?: (PermissionStatus | null),
     ): CancelablePromise<Array<BucketPermissionOut>> {
         return __request(OpenAPI, {
             method: 'GET',
diff --git a/src/client/s3proxy/services/BucketService.ts b/src/client/s3proxy/services/BucketService.ts
index a56c3ccf0b37e5f164d1ae1eae0e562ce559e5c0..20d718fb164bcfa06c2102633687ee381d6d37ea 100644
--- a/src/client/s3proxy/services/BucketService.ts
+++ b/src/client/s3proxy/services/BucketService.ts
@@ -1,3 +1,4 @@
+/* generated using openapi-typescript-codegen -- do no edit */
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
@@ -23,7 +24,7 @@ export class BucketService {
      * @throws ApiError
      */
     public static bucketListBuckets(
-        user?: string,
+        user?: (string | null),
         bucketType?: BucketType,
     ): CancelablePromise<Array<BucketOut>> {
         return __request(OpenAPI, {
@@ -134,6 +135,7 @@ export class BucketService {
     }
 
     /**
+     * @deprecated
      * Get the metadata of the objects in the bucket
      * Get the metadata of the objects in the bucket.
      *
@@ -149,7 +151,7 @@ export class BucketService {
      */
     public static objectGetBucketObjects(
         bucketName: string,
-        filePrefix?: string,
+        filePrefix?: (string | null),
     ): CancelablePromise<Array<S3ObjectMetaInformation>> {
         return __request(OpenAPI, {
             method: 'GET',
@@ -170,6 +172,7 @@ export class BucketService {
     }
 
     /**
+     * @deprecated
      * Get the metadata about a specific object
      * Get the metadata of a specific object in a bucket.
      *
diff --git a/src/client/s3proxy/services/ObjectService.ts b/src/client/s3proxy/services/ObjectService.ts
index ae0716a96fa8b8709d0b730ab04a6d47a6e5cf51..0df2a8345aa3fdba1c99846f934f3035660a5c72 100644
--- a/src/client/s3proxy/services/ObjectService.ts
+++ b/src/client/s3proxy/services/ObjectService.ts
@@ -1,3 +1,4 @@
+/* generated using openapi-typescript-codegen -- do no edit */
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
@@ -10,6 +11,7 @@ import { request as __request } from '../core/request';
 export class ObjectService {
 
     /**
+     * @deprecated
      * Get the metadata of the objects in the bucket
      * Get the metadata of the objects in the bucket.
      *
@@ -25,7 +27,7 @@ export class ObjectService {
      */
     public static objectGetBucketObjects(
         bucketName: string,
-        filePrefix?: string,
+        filePrefix?: (string | null),
     ): CancelablePromise<Array<S3ObjectMetaInformation>> {
         return __request(OpenAPI, {
             method: 'GET',
@@ -46,6 +48,7 @@ export class ObjectService {
     }
 
     /**
+     * @deprecated
      * Get the metadata about a specific object
      * Get the metadata of a specific object in a bucket.
      *
diff --git a/src/client/s3proxy/services/S3KeyService.ts b/src/client/s3proxy/services/S3KeyService.ts
index 5d052a7927e3d4f2ba643fe31515856580e2406b..c8e048bdc22fbbd683259c5850045063938a7aee 100644
--- a/src/client/s3proxy/services/S3KeyService.ts
+++ b/src/client/s3proxy/services/S3KeyService.ts
@@ -1,3 +1,4 @@
+/* generated using openapi-typescript-codegen -- do no edit */
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
diff --git a/src/client/workflow/core/ApiError.ts b/src/client/workflow/core/ApiError.ts
index 99d792996765118614adf6a232bc8766b95710e9..d6b8fcc3ad0b6b2bdf1aa4df97ec598e64995648 100644
--- a/src/client/workflow/core/ApiError.ts
+++ b/src/client/workflow/core/ApiError.ts
@@ -1,3 +1,4 @@
+/* generated using openapi-typescript-codegen -- do no edit */
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
diff --git a/src/client/workflow/core/ApiRequestOptions.ts b/src/client/workflow/core/ApiRequestOptions.ts
index c7b77538c5ce6aa48f141541392121fbcafc4821..c19adcc94dc5f015865368d6d64751de5c243aa3 100644
--- a/src/client/workflow/core/ApiRequestOptions.ts
+++ b/src/client/workflow/core/ApiRequestOptions.ts
@@ -1,3 +1,4 @@
+/* generated using openapi-typescript-codegen -- do no edit */
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
diff --git a/src/client/workflow/core/ApiResult.ts b/src/client/workflow/core/ApiResult.ts
index b095dc7708af2aa405a6dc7ad08f423334bc7f24..ad8fef2bc334c2aedf5c043896fd394229453ff0 100644
--- a/src/client/workflow/core/ApiResult.ts
+++ b/src/client/workflow/core/ApiResult.ts
@@ -1,3 +1,4 @@
+/* generated using openapi-typescript-codegen -- do no edit */
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
diff --git a/src/client/workflow/core/CancelablePromise.ts b/src/client/workflow/core/CancelablePromise.ts
index 26ad303915af161f170cc144a5e7684be3053778..55fef8517238d2dab7478598eefca72e657b9fb5 100644
--- a/src/client/workflow/core/CancelablePromise.ts
+++ b/src/client/workflow/core/CancelablePromise.ts
@@ -1,3 +1,4 @@
+/* generated using openapi-typescript-codegen -- do no edit */
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
@@ -22,15 +23,13 @@ export interface OnCancel {
 }
 
 export class CancelablePromise<T> implements Promise<T> {
-    readonly [Symbol.toStringTag]!: string;
-
-    private _isResolved: boolean;
-    private _isRejected: boolean;
-    private _isCancelled: boolean;
-    private readonly _cancelHandlers: (() => void)[];
-    private readonly _promise: Promise<T>;
-    private _resolve?: (value: T | PromiseLike<T>) => void;
-    private _reject?: (reason?: any) => void;
+    #isResolved: boolean;
+    #isRejected: boolean;
+    #isCancelled: boolean;
+    readonly #cancelHandlers: (() => void)[];
+    readonly #promise: Promise<T>;
+    #resolve?: (value: T | PromiseLike<T>) => void;
+    #reject?: (reason?: any) => void;
 
     constructor(
         executor: (
@@ -39,78 +38,82 @@ export class CancelablePromise<T> implements Promise<T> {
             onCancel: OnCancel
         ) => void
     ) {
-        this._isResolved = false;
-        this._isRejected = false;
-        this._isCancelled = false;
-        this._cancelHandlers = [];
-        this._promise = new Promise<T>((resolve, reject) => {
-            this._resolve = resolve;
-            this._reject = reject;
+        this.#isResolved = false;
+        this.#isRejected = false;
+        this.#isCancelled = false;
+        this.#cancelHandlers = [];
+        this.#promise = new Promise<T>((resolve, reject) => {
+            this.#resolve = resolve;
+            this.#reject = reject;
 
             const onResolve = (value: T | PromiseLike<T>): void => {
-                if (this._isResolved || this._isRejected || this._isCancelled) {
+                if (this.#isResolved || this.#isRejected || this.#isCancelled) {
                     return;
                 }
-                this._isResolved = true;
-                this._resolve?.(value);
+                this.#isResolved = true;
+                this.#resolve?.(value);
             };
 
             const onReject = (reason?: any): void => {
-                if (this._isResolved || this._isRejected || this._isCancelled) {
+                if (this.#isResolved || this.#isRejected || this.#isCancelled) {
                     return;
                 }
-                this._isRejected = true;
-                this._reject?.(reason);
+                this.#isRejected = true;
+                this.#reject?.(reason);
             };
 
             const onCancel = (cancelHandler: () => void): void => {
-                if (this._isResolved || this._isRejected || this._isCancelled) {
+                if (this.#isResolved || this.#isRejected || this.#isCancelled) {
                     return;
                 }
-                this._cancelHandlers.push(cancelHandler);
+                this.#cancelHandlers.push(cancelHandler);
             };
 
             Object.defineProperty(onCancel, 'isResolved', {
-                get: (): boolean => this._isResolved,
+                get: (): boolean => this.#isResolved,
             });
 
             Object.defineProperty(onCancel, 'isRejected', {
-                get: (): boolean => this._isRejected,
+                get: (): boolean => this.#isRejected,
             });
 
             Object.defineProperty(onCancel, 'isCancelled', {
-                get: (): boolean => this._isCancelled,
+                get: (): boolean => this.#isCancelled,
             });
 
             return executor(onResolve, onReject, onCancel as OnCancel);
         });
     }
 
+     get [Symbol.toStringTag]() {
+            return "Cancellable Promise";
+     }
+
     public then<TResult1 = T, TResult2 = never>(
         onFulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | null,
         onRejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null
     ): Promise<TResult1 | TResult2> {
-        return this._promise.then(onFulfilled, onRejected);
+        return this.#promise.then(onFulfilled, onRejected);
     }
 
     public catch<TResult = never>(
         onRejected?: ((reason: any) => TResult | PromiseLike<TResult>) | null
     ): Promise<T | TResult> {
-        return this._promise.catch(onRejected);
+        return this.#promise.catch(onRejected);
     }
 
     public finally(onFinally?: (() => void) | null): Promise<T> {
-        return this._promise.finally(onFinally);
+        return this.#promise.finally(onFinally);
     }
 
     public cancel(): void {
-        if (this._isResolved || this._isRejected || this._isCancelled) {
+        if (this.#isResolved || this.#isRejected || this.#isCancelled) {
             return;
         }
-        this._isCancelled = true;
-        if (this._cancelHandlers.length) {
+        this.#isCancelled = true;
+        if (this.#cancelHandlers.length) {
             try {
-                for (const cancelHandler of this._cancelHandlers) {
+                for (const cancelHandler of this.#cancelHandlers) {
                     cancelHandler();
                 }
             } catch (error) {
@@ -118,11 +121,11 @@ export class CancelablePromise<T> implements Promise<T> {
                 return;
             }
         }
-        this._cancelHandlers.length = 0;
-        this._reject?.(new CancelError('Request aborted'));
+        this.#cancelHandlers.length = 0;
+        this.#reject?.(new CancelError('Request aborted'));
     }
 
     public get isCancelled(): boolean {
-        return this._isCancelled;
+        return this.#isCancelled;
     }
 }
diff --git a/src/client/workflow/core/OpenAPI.ts b/src/client/workflow/core/OpenAPI.ts
index 809eda09cde31266ee99deeec2e0b53159c5a8e7..af264be25a464af3a2b3dcc57232c87766ac0180 100644
--- a/src/client/workflow/core/OpenAPI.ts
+++ b/src/client/workflow/core/OpenAPI.ts
@@ -1,3 +1,4 @@
+/* generated using openapi-typescript-codegen -- do no edit */
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
@@ -11,11 +12,11 @@ export type OpenAPIConfig = {
     VERSION: string;
     WITH_CREDENTIALS: boolean;
     CREDENTIALS: 'include' | 'omit' | 'same-origin';
-    TOKEN?: string | Resolver<string>;
-    USERNAME?: string | Resolver<string>;
-    PASSWORD?: string | Resolver<string>;
-    HEADERS?: Headers | Resolver<Headers>;
-    ENCODE_PATH?: (path: string) => string;
+    TOKEN?: string | Resolver<string> | undefined;
+    USERNAME?: string | Resolver<string> | undefined;
+    PASSWORD?: string | Resolver<string> | undefined;
+    HEADERS?: Headers | Resolver<Headers> | undefined;
+    ENCODE_PATH?: ((path: string) => string) | undefined;
 };
 
 export const OpenAPI: OpenAPIConfig = {
diff --git a/src/client/workflow/core/request.ts b/src/client/workflow/core/request.ts
index 2b5375ab44ec20863646a5debca820b42c2b7500..1142d43219797c94e09f35ba83a9f5441892ddd2 100644
--- a/src/client/workflow/core/request.ts
+++ b/src/client/workflow/core/request.ts
@@ -1,8 +1,9 @@
+/* generated using openapi-typescript-codegen -- do no edit */
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
 import axios from 'axios';
-import type { AxiosError, AxiosRequestConfig, AxiosResponse } from 'axios';
+import type { AxiosError, AxiosRequestConfig, AxiosResponse, AxiosInstance } from 'axios';
 import FormData from 'form-data';
 
 import { ApiError } from './ApiError';
@@ -12,19 +13,19 @@ import { CancelablePromise } from './CancelablePromise';
 import type { OnCancel } from './CancelablePromise';
 import type { OpenAPIConfig } from './OpenAPI';
 
-const isDefined = <T>(value: T | null | undefined): value is Exclude<T, null | undefined> => {
+export const isDefined = <T>(value: T | null | undefined): value is Exclude<T, null | undefined> => {
     return value !== undefined && value !== null;
 };
 
-const isString = (value: any): value is string => {
+export const isString = (value: any): value is string => {
     return typeof value === 'string';
 };
 
-const isStringWithValue = (value: any): value is string => {
+export const isStringWithValue = (value: any): value is string => {
     return isString(value) && value !== '';
 };
 
-const isBlob = (value: any): value is Blob => {
+export const isBlob = (value: any): value is Blob => {
     return (
         typeof value === 'object' &&
         typeof value.type === 'string' &&
@@ -37,15 +38,15 @@ const isBlob = (value: any): value is Blob => {
     );
 };
 
-const isFormData = (value: any): value is FormData => {
+export const isFormData = (value: any): value is FormData => {
     return value instanceof FormData;
 };
 
-const isSuccess = (status: number): boolean => {
+export const isSuccess = (status: number): boolean => {
     return status >= 200 && status < 300;
 };
 
-const base64 = (str: string): string => {
+export const base64 = (str: string): string => {
     try {
         return btoa(str);
     } catch (err) {
@@ -54,7 +55,7 @@ const base64 = (str: string): string => {
     }
 };
 
-const getQueryString = (params: Record<string, any>): string => {
+export const getQueryString = (params: Record<string, any>): string => {
     const qs: string[] = [];
 
     const append = (key: string, value: any) => {
@@ -107,7 +108,7 @@ const getUrl = (config: OpenAPIConfig, options: ApiRequestOptions): string => {
     return url;
 };
 
-const getFormData = (options: ApiRequestOptions): FormData | undefined => {
+export const getFormData = (options: ApiRequestOptions): FormData | undefined => {
     if (options.formData) {
         const formData = new FormData();
 
@@ -136,14 +137,14 @@ const getFormData = (options: ApiRequestOptions): FormData | undefined => {
 
 type Resolver<T> = (options: ApiRequestOptions) => Promise<T>;
 
-const resolve = async <T>(options: ApiRequestOptions, resolver?: T | Resolver<T>): Promise<T | undefined> => {
+export const resolve = async <T>(options: ApiRequestOptions, resolver?: T | Resolver<T>): Promise<T | undefined> => {
     if (typeof resolver === 'function') {
         return (resolver as Resolver<T>)(options);
     }
     return resolver;
 };
 
-const getHeaders = async (config: OpenAPIConfig, options: ApiRequestOptions, formData?: FormData): Promise<Record<string, string>> => {
+export const getHeaders = async (config: OpenAPIConfig, options: ApiRequestOptions, formData?: FormData): Promise<Record<string, string>> => {
     const token = await resolve(options, config.TOKEN);
     const username = await resolve(options, config.USERNAME);
     const password = await resolve(options, config.PASSWORD);
@@ -186,21 +187,22 @@ const getHeaders = async (config: OpenAPIConfig, options: ApiRequestOptions, for
     return headers;
 };
 
-const getRequestBody = (options: ApiRequestOptions): any => {
+export const getRequestBody = (options: ApiRequestOptions): any => {
     if (options.body) {
         return options.body;
     }
     return undefined;
 };
 
-const sendRequest = async <T>(
+export const sendRequest = async <T>(
     config: OpenAPIConfig,
     options: ApiRequestOptions,
     url: string,
     body: any,
     formData: FormData | undefined,
     headers: Record<string, string>,
-    onCancel: OnCancel
+    onCancel: OnCancel,
+    axiosClient: AxiosInstance
 ): Promise<AxiosResponse<T>> => {
     const source = axios.CancelToken.source();
 
@@ -216,7 +218,7 @@ const sendRequest = async <T>(
     onCancel(() => source.cancel('The user aborted a request.'));
 
     try {
-        return await axios.request(requestConfig);
+        return await axiosClient.request(requestConfig);
     } catch (error) {
         const axiosError = error as AxiosError<T>;
         if (axiosError.response) {
@@ -226,7 +228,7 @@ const sendRequest = async <T>(
     }
 };
 
-const getResponseHeader = (response: AxiosResponse<any>, responseHeader?: string): string | undefined => {
+export const getResponseHeader = (response: AxiosResponse<any>, responseHeader?: string): string | undefined => {
     if (responseHeader) {
         const content = response.headers[responseHeader];
         if (isString(content)) {
@@ -236,14 +238,14 @@ const getResponseHeader = (response: AxiosResponse<any>, responseHeader?: string
     return undefined;
 };
 
-const getResponseBody = (response: AxiosResponse<any>): any => {
+export const getResponseBody = (response: AxiosResponse<any>): any => {
     if (response.status !== 204) {
         return response.data;
     }
     return undefined;
 };
 
-const catchErrorCodes = (options: ApiRequestOptions, result: ApiResult): void => {
+export const catchErrorCodes = (options: ApiRequestOptions, result: ApiResult): void => {
     const errors: Record<number, string> = {
         400: 'Bad Request',
         401: 'Unauthorized',
@@ -261,7 +263,19 @@ const catchErrorCodes = (options: ApiRequestOptions, result: ApiResult): void =>
     }
 
     if (!result.ok) {
-        throw new ApiError(options, result, 'Generic Error');
+        const errorStatus = result.status ?? 'unknown';
+        const errorStatusText = result.statusText ?? 'unknown';
+        const errorBody = (() => {
+            try {
+                return JSON.stringify(result.body, null, 2);
+            } catch (e) {
+                return undefined;
+            }
+        })();
+
+        throw new ApiError(options, result,
+            `Generic Error: status: ${errorStatus}; status text: ${errorStatusText}; body: ${errorBody}`
+        );
     }
 };
 
@@ -269,10 +283,11 @@ const catchErrorCodes = (options: ApiRequestOptions, result: ApiResult): void =>
  * Request method
  * @param config The OpenAPI configuration object
  * @param options The request options from the service
+ * @param axiosClient The axios client instance to use
  * @returns CancelablePromise<T>
  * @throws ApiError
  */
-export const request = <T>(config: OpenAPIConfig, options: ApiRequestOptions): CancelablePromise<T> => {
+export const request = <T>(config: OpenAPIConfig, options: ApiRequestOptions, axiosClient: AxiosInstance = axios): CancelablePromise<T> => {
     return new CancelablePromise(async (resolve, reject, onCancel) => {
         try {
             const url = getUrl(config, options);
@@ -281,7 +296,7 @@ export const request = <T>(config: OpenAPIConfig, options: ApiRequestOptions): C
             const headers = await getHeaders(config, options, formData);
 
             if (!onCancel.isCancelled) {
-                const response = await sendRequest<T>(config, options, url, body, formData, headers, onCancel);
+                const response = await sendRequest<T>(config, options, url, body, formData, headers, onCancel, axiosClient);
                 const responseBody = getResponseBody(response);
                 const responseHeader = getResponseHeader(response, options.responseHeader);
 
diff --git a/src/client/workflow/index.ts b/src/client/workflow/index.ts
index cb6c872427f3cb7150fc2c4688b974cc40e20c42..3b82994fee1718758b8ac10abf07efc931577860 100644
--- a/src/client/workflow/index.ts
+++ b/src/client/workflow/index.ts
@@ -1,3 +1,4 @@
+/* generated using openapi-typescript-codegen -- do no edit */
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
@@ -6,22 +7,30 @@ export { CancelablePromise, CancelError } from './core/CancelablePromise';
 export { OpenAPI } from './core/OpenAPI';
 export type { OpenAPIConfig } from './core/OpenAPI';
 
-export type { Body_Workflow_create_workflow } from './models/Body_Workflow_create_workflow';
-export type { Body_Workflow_update_workflow } from './models/Body_Workflow_update_workflow';
+export type { Body_Workflow_Version_upload_workflow_version_icon } from './models/Body_Workflow_Version_upload_workflow_version_icon';
 export type { DevWorkflowExecutionIn } from './models/DevWorkflowExecutionIn';
+export { DocumentationEnum } from './models/DocumentationEnum';
 export type { ErrorDetail } from './models/ErrorDetail';
 export type { HTTPValidationError } from './models/HTTPValidationError';
+export type { IconUpdateOut } from './models/IconUpdateOut';
 export { Status } from './models/Status';
 export type { ValidationError } from './models/ValidationError';
+export type { WorkflowCredentialsIn } from './models/WorkflowCredentialsIn';
+export type { WorkflowCredentialsOut } from './models/WorkflowCredentialsOut';
 export type { WorkflowExecutionIn } from './models/WorkflowExecutionIn';
 export type { WorkflowExecutionOut } from './models/WorkflowExecutionOut';
 export { WorkflowExecutionStatus } from './models/WorkflowExecutionStatus';
+export type { WorkflowIn } from './models/WorkflowIn';
+export type { WorkflowModeIn } from './models/WorkflowModeIn';
+export type { WorkflowModeOut } from './models/WorkflowModeOut';
 export type { WorkflowOut } from './models/WorkflowOut';
 export type { WorkflowStatistic } from './models/WorkflowStatistic';
-export type { WorkflowVersionFull } from './models/WorkflowVersionFull';
-export type { WorkflowVersionReduced } from './models/WorkflowVersionReduced';
+export type { WorkflowUpdate } from './models/WorkflowUpdate';
+export type { WorkflowVersion } from './models/WorkflowVersion';
 export type { WorkflowVersionStatus } from './models/WorkflowVersionStatus';
 
 export { WorkflowService } from './services/WorkflowService';
+export { WorkflowCredentialsService } from './services/WorkflowCredentialsService';
 export { WorkflowExecutionService } from './services/WorkflowExecutionService';
+export { WorkflowModeService } from './services/WorkflowModeService';
 export { WorkflowVersionService } from './services/WorkflowVersionService';
diff --git a/src/client/workflow/models/Body_Workflow_Version_upload_workflow_version_icon.ts b/src/client/workflow/models/Body_Workflow_Version_upload_workflow_version_icon.ts
new file mode 100644
index 0000000000000000000000000000000000000000..208b0c24d5b34514fe1156aafeddabd72645a53c
--- /dev/null
+++ b/src/client/workflow/models/Body_Workflow_Version_upload_workflow_version_icon.ts
@@ -0,0 +1,12 @@
+/* generated using openapi-typescript-codegen -- do no edit */
+/* istanbul ignore file */
+/* tslint:disable */
+/* eslint-disable */
+
+export type Body_Workflow_Version_upload_workflow_version_icon = {
+    /**
+     * Optional Icon for the Workflow.
+     */
+    icon: Blob;
+};
+
diff --git a/src/client/workflow/models/Body_Workflow_create_workflow.ts b/src/client/workflow/models/Body_Workflow_create_workflow.ts
deleted file mode 100644
index f5e33b0c935c1cc3e22c08ffe3a205b2c4dc441e..0000000000000000000000000000000000000000
--- a/src/client/workflow/models/Body_Workflow_create_workflow.ts
+++ /dev/null
@@ -1,16 +0,0 @@
-/* istanbul ignore file */
-/* tslint:disable */
-/* eslint-disable */
-
-export type Body_Workflow_create_workflow = {
-    /**
-     * Optional Icon for the Workflow.
-     */
-    icon?: Blob;
-    name: string;
-    short_description: string;
-    repository_url: string;
-    git_commit_hash: string;
-    initial_version?: string;
-};
-
diff --git a/src/client/workflow/models/Body_Workflow_update_workflow.ts b/src/client/workflow/models/Body_Workflow_update_workflow.ts
deleted file mode 100644
index 9c0abe315adff9adeb2990f6eb856be6e415f1ef..0000000000000000000000000000000000000000
--- a/src/client/workflow/models/Body_Workflow_update_workflow.ts
+++ /dev/null
@@ -1,13 +0,0 @@
-/* istanbul ignore file */
-/* tslint:disable */
-/* eslint-disable */
-
-export type Body_Workflow_update_workflow = {
-    /**
-     * Optional Icon for the workflow version. If None, then the previous one will be reused.
-     */
-    icon?: Blob;
-    version: string;
-    git_commit_hash: string;
-};
-
diff --git a/src/client/workflow/models/DevWorkflowExecutionIn.ts b/src/client/workflow/models/DevWorkflowExecutionIn.ts
index b48bb736dfaa6897fe4dfba88b18b7f22645db24..7e69f9c33628b53f763c5a299d320e892c4ec4d9 100644
--- a/src/client/workflow/models/DevWorkflowExecutionIn.ts
+++ b/src/client/workflow/models/DevWorkflowExecutionIn.ts
@@ -1,16 +1,19 @@
+/* generated using openapi-typescript-codegen -- do no edit */
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
 
+import type { WorkflowModeIn } from './WorkflowModeIn';
+
 export type DevWorkflowExecutionIn = {
     /**
      * Parameters for this workflow
      */
-    parameters: any;
+    parameters: Record<string, any>;
     /**
      * Bucket where to save the Nextflow report. If None, no report will be generated
      */
-    report_output_bucket?: string;
+    report_output_bucket?: (string | null);
     /**
      * Hash of the git commit
      */
@@ -19,5 +22,13 @@ export type DevWorkflowExecutionIn = {
      * URL to the Git repository belonging to this workflow
      */
     repository_url: string;
+    /**
+     * Token to access the content git repository
+     */
+    token?: (string | null);
+    /**
+     * Mode of the workflow with an alternative entrypoint
+     */
+    mode?: (WorkflowModeIn | null);
 };
 
diff --git a/src/client/workflow/models/DocumentationEnum.ts b/src/client/workflow/models/DocumentationEnum.ts
new file mode 100644
index 0000000000000000000000000000000000000000..1ae04856a01b1b0b6dfe8c550ec97720aaeb4cd6
--- /dev/null
+++ b/src/client/workflow/models/DocumentationEnum.ts
@@ -0,0 +1,12 @@
+/* generated using openapi-typescript-codegen -- do no edit */
+/* istanbul ignore file */
+/* tslint:disable */
+/* eslint-disable */
+
+export enum DocumentationEnum {
+    USAGE = 'usage',
+    INPUT = 'input',
+    OUTPUT = 'output',
+    CHANGELOG = 'changelog',
+    PARAMETER_SCHEMA = 'parameter_schema',
+}
diff --git a/src/client/workflow/models/ErrorDetail.ts b/src/client/workflow/models/ErrorDetail.ts
index b01e5aa28734c901620e98f8795e9528004d1513..eba50ab9335b70df9b29cada4c54cf9ffaad10a4 100644
--- a/src/client/workflow/models/ErrorDetail.ts
+++ b/src/client/workflow/models/ErrorDetail.ts
@@ -1,3 +1,4 @@
+/* generated using openapi-typescript-codegen -- do no edit */
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
diff --git a/src/client/workflow/models/HTTPValidationError.ts b/src/client/workflow/models/HTTPValidationError.ts
index a8c21354123500873c7548194a36421eb8316c78..c0bcc87cf7f3222638466bc2e5753ca1accf01f7 100644
--- a/src/client/workflow/models/HTTPValidationError.ts
+++ b/src/client/workflow/models/HTTPValidationError.ts
@@ -1,3 +1,4 @@
+/* generated using openapi-typescript-codegen -- do no edit */
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
diff --git a/src/client/workflow/models/IconUpdateOut.ts b/src/client/workflow/models/IconUpdateOut.ts
new file mode 100644
index 0000000000000000000000000000000000000000..0499796ef14433d99fae156f3508df5ed0db9984
--- /dev/null
+++ b/src/client/workflow/models/IconUpdateOut.ts
@@ -0,0 +1,12 @@
+/* generated using openapi-typescript-codegen -- do no edit */
+/* istanbul ignore file */
+/* tslint:disable */
+/* eslint-disable */
+
+export type IconUpdateOut = {
+    /**
+     * URL to the uploaded icon
+     */
+    icon_url: string;
+};
+
diff --git a/src/client/workflow/models/Status.ts b/src/client/workflow/models/Status.ts
index 6629a4080e6eb4520e1c54f0219955484d30a2ee..8da66ea5e286d86b0f4d9d1c3b7bac8167d769ab 100644
--- a/src/client/workflow/models/Status.ts
+++ b/src/client/workflow/models/Status.ts
@@ -1,3 +1,4 @@
+/* generated using openapi-typescript-codegen -- do no edit */
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
diff --git a/src/client/workflow/models/ValidationError.ts b/src/client/workflow/models/ValidationError.ts
index 16dc86a6807d9ad1f7e9d06638d4bd002682dc42..18997ec72f4103731f38d915508522ba23ba8506 100644
--- a/src/client/workflow/models/ValidationError.ts
+++ b/src/client/workflow/models/ValidationError.ts
@@ -1,3 +1,4 @@
+/* generated using openapi-typescript-codegen -- do no edit */
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
diff --git a/src/client/workflow/models/WorkflowCredentialsIn.ts b/src/client/workflow/models/WorkflowCredentialsIn.ts
new file mode 100644
index 0000000000000000000000000000000000000000..37723a3e3a9384f04e6d52ecd6ac4f302d6ac304
--- /dev/null
+++ b/src/client/workflow/models/WorkflowCredentialsIn.ts
@@ -0,0 +1,12 @@
+/* generated using openapi-typescript-codegen -- do no edit */
+/* istanbul ignore file */
+/* tslint:disable */
+/* eslint-disable */
+
+export type WorkflowCredentialsIn = {
+    /**
+     * Token to access the content git repository
+     */
+    token: string;
+};
+
diff --git a/src/client/workflow/models/WorkflowCredentialsOut.ts b/src/client/workflow/models/WorkflowCredentialsOut.ts
new file mode 100644
index 0000000000000000000000000000000000000000..3b669c25db50ce191e19e90ff62ce1b18a0447c3
--- /dev/null
+++ b/src/client/workflow/models/WorkflowCredentialsOut.ts
@@ -0,0 +1,12 @@
+/* generated using openapi-typescript-codegen -- do no edit */
+/* istanbul ignore file */
+/* tslint:disable */
+/* eslint-disable */
+
+export type WorkflowCredentialsOut = {
+    /**
+     * Token to access the content git repository
+     */
+    token: (string | null);
+};
+
diff --git a/src/client/workflow/models/WorkflowExecutionIn.ts b/src/client/workflow/models/WorkflowExecutionIn.ts
index 60d3b37c8c6c90f304fe8ff1fed86e9adddd3731..e61ac3d63f55e2f48c7076f73fc3e5dc9764c99c 100644
--- a/src/client/workflow/models/WorkflowExecutionIn.ts
+++ b/src/client/workflow/models/WorkflowExecutionIn.ts
@@ -1,3 +1,4 @@
+/* generated using openapi-typescript-codegen -- do no edit */
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
@@ -10,14 +11,18 @@ export type WorkflowExecutionIn = {
     /**
      * Optional notes for this workflow execution
      */
-    notes?: string;
+    notes?: (string | null);
+    /**
+     * ID of the workflow mode this workflow execution runs in
+     */
+    mode?: (string | null);
     /**
      * Parameters for this workflow
      */
-    parameters: any;
+    parameters: Record<string, any>;
     /**
-     * Bucket where to save the Nextflow report. If None, no report will be generated. With our without prefix 's3://'
+     * Bucket where to save the Nextflow report. If None, no report will be generated. With or without prefix 's3://'
      */
-    report_output_bucket?: string;
+    report_output_bucket?: (string | null);
 };
 
diff --git a/src/client/workflow/models/WorkflowExecutionOut.ts b/src/client/workflow/models/WorkflowExecutionOut.ts
index 8561ab6dba292e57cb31a52c74997303d1e34ac5..9532a9c0841cf85cc587abd91367e6755bdf8596 100644
--- a/src/client/workflow/models/WorkflowExecutionOut.ts
+++ b/src/client/workflow/models/WorkflowExecutionOut.ts
@@ -1,3 +1,4 @@
+/* generated using openapi-typescript-codegen -- do no edit */
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
@@ -8,11 +9,15 @@ export type WorkflowExecutionOut = {
     /**
      * Workflow version git commit hash
      */
-    workflow_version_id: string;
+    workflow_version_id: (string | null);
     /**
      * Optional notes for this workflow execution
      */
-    notes?: string;
+    notes: (string | null);
+    /**
+     * ID of the workflow mode this workflow execution runs in
+     */
+    mode: (string | null);
     /**
      * ID of the workflow execution
      */
@@ -22,13 +27,13 @@ export type WorkflowExecutionOut = {
      */
     user_id: string;
     /**
-     * Start time of the workflow execution
+     * Start time of the workflow execution as UNIX timestamp
      */
-    start_time: string;
+    start_time: number;
     /**
-     * End time of the workflow execution
+     * End time of the workflow execution as UNIX timestamp
      */
-    end_time?: string;
+    end_time: (number | null);
     /**
      * Status of the workflow execution
      */
@@ -36,6 +41,6 @@ export type WorkflowExecutionOut = {
     /**
      * Id of the workflow
      */
-    workflow_id: string;
+    workflow_id: (string | null);
 };
 
diff --git a/src/client/workflow/models/WorkflowExecutionStatus.ts b/src/client/workflow/models/WorkflowExecutionStatus.ts
index 9b090d159dca2ac1bfe959eb6bcff35193748088..dee8c8fea4f0bbb84f2b8aaf69f626989e1639a6 100644
--- a/src/client/workflow/models/WorkflowExecutionStatus.ts
+++ b/src/client/workflow/models/WorkflowExecutionStatus.ts
@@ -1,3 +1,4 @@
+/* generated using openapi-typescript-codegen -- do no edit */
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
diff --git a/src/client/workflow/models/WorkflowIn.ts b/src/client/workflow/models/WorkflowIn.ts
new file mode 100644
index 0000000000000000000000000000000000000000..544b0e3d50e28167d433cf1dda6a1bfaef7d54f6
--- /dev/null
+++ b/src/client/workflow/models/WorkflowIn.ts
@@ -0,0 +1,38 @@
+/* generated using openapi-typescript-codegen -- do no edit */
+/* istanbul ignore file */
+/* tslint:disable */
+/* eslint-disable */
+
+import type { WorkflowModeIn } from './WorkflowModeIn';
+
+export type WorkflowIn = {
+    /**
+     * Short descriptive name of the workflow
+     */
+    name: string;
+    /**
+     * Short description of the workflow
+     */
+    short_description: string;
+    /**
+     * URL to the Git repository belonging to this workflow
+     */
+    repository_url: string;
+    /**
+     * Hash of the git commit
+     */
+    git_commit_hash: string;
+    /**
+     * Initial version of the Workflow. Should follow semantic versioning
+     */
+    initial_version?: string;
+    /**
+     * Token to access the content git repository
+     */
+    token?: (string | null);
+    /**
+     * List of modes with alternative entrypoint the new workflow has
+     */
+    modes?: (Array<WorkflowModeIn> | null);
+};
+
diff --git a/src/client/workflow/models/WorkflowModeIn.ts b/src/client/workflow/models/WorkflowModeIn.ts
new file mode 100644
index 0000000000000000000000000000000000000000..a50b038c1abd7c60352b1526e164c652aa99e7df
--- /dev/null
+++ b/src/client/workflow/models/WorkflowModeIn.ts
@@ -0,0 +1,20 @@
+/* generated using openapi-typescript-codegen -- do no edit */
+/* istanbul ignore file */
+/* tslint:disable */
+/* eslint-disable */
+
+export type WorkflowModeIn = {
+    /**
+     * Path to the alternative parameter schema
+     */
+    schema_path: string;
+    /**
+     * Name of the process the workflow should start with. Argument to the parameter '-entry'
+     */
+    entrypoint: string;
+    /**
+     * Name of the workflow mode
+     */
+    name: string;
+};
+
diff --git a/src/client/workflow/models/WorkflowModeOut.ts b/src/client/workflow/models/WorkflowModeOut.ts
new file mode 100644
index 0000000000000000000000000000000000000000..1d52e08bd2580b58af3b5204a6e6ebc1fa2d6ee8
--- /dev/null
+++ b/src/client/workflow/models/WorkflowModeOut.ts
@@ -0,0 +1,24 @@
+/* generated using openapi-typescript-codegen -- do no edit */
+/* istanbul ignore file */
+/* tslint:disable */
+/* eslint-disable */
+
+export type WorkflowModeOut = {
+    /**
+     * Path to the alternative parameter schema
+     */
+    schema_path: string;
+    /**
+     * Name of the process the workflow should start with. Argument to the parameter '-entry'
+     */
+    entrypoint: string;
+    /**
+     * Name of the workflow mode
+     */
+    name: string;
+    /**
+     * ID of the workflow mode
+     */
+    mode_id: string;
+};
+
diff --git a/src/client/workflow/models/WorkflowOut.ts b/src/client/workflow/models/WorkflowOut.ts
index b16961cff5439bd2817580383ca376f5f772af58..a9c6a081189c37ba74c4e5a8db0307fd29d2e07a 100644
--- a/src/client/workflow/models/WorkflowOut.ts
+++ b/src/client/workflow/models/WorkflowOut.ts
@@ -1,8 +1,9 @@
+/* generated using openapi-typescript-codegen -- do no edit */
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
 
-import type { WorkflowVersionReduced } from './WorkflowVersionReduced';
+import type { WorkflowVersion } from './WorkflowVersion';
 
 export type WorkflowOut = {
     /**
@@ -24,10 +25,14 @@ export type WorkflowOut = {
     /**
      * Versions of the workflow
      */
-    versions: Array<WorkflowVersionReduced>;
+    versions: Array<WorkflowVersion>;
     /**
      * Id of developer of the workflow
      */
     developer_id: string;
+    /**
+     * Flag if the workflow is hosted in a private git repository
+     */
+    private: boolean;
 };
 
diff --git a/src/client/workflow/models/WorkflowStatistic.ts b/src/client/workflow/models/WorkflowStatistic.ts
index e9211ec655b249f38790bed6eadf850e6465b7dc..4b7fbade791196f814a52cee40e8cca117ea8102 100644
--- a/src/client/workflow/models/WorkflowStatistic.ts
+++ b/src/client/workflow/models/WorkflowStatistic.ts
@@ -1,3 +1,4 @@
+/* generated using openapi-typescript-codegen -- do no edit */
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
diff --git a/src/client/workflow/models/WorkflowUpdate.ts b/src/client/workflow/models/WorkflowUpdate.ts
new file mode 100644
index 0000000000000000000000000000000000000000..97c2577769795b6fc537326f61b7772fc98ab668
--- /dev/null
+++ b/src/client/workflow/models/WorkflowUpdate.ts
@@ -0,0 +1,26 @@
+/* generated using openapi-typescript-codegen -- do no edit */
+/* istanbul ignore file */
+/* tslint:disable */
+/* eslint-disable */
+
+import type { WorkflowModeIn } from './WorkflowModeIn';
+
+export type WorkflowUpdate = {
+    /**
+     * Version of the Workflow. Should follow semantic versioning
+     */
+    version: string;
+    /**
+     * Hash of the git commit
+     */
+    git_commit_hash: string;
+    /**
+     * Add modes to the new workflow version
+     */
+    append_modes?: (Array<WorkflowModeIn> | null);
+    /**
+     * Delete modes for the new workflow version.
+     */
+    delete_modes?: (Array<string> | null);
+};
+
diff --git a/src/client/workflow/models/WorkflowVersionReduced.ts b/src/client/workflow/models/WorkflowVersion.ts
similarity index 53%
rename from src/client/workflow/models/WorkflowVersionReduced.ts
rename to src/client/workflow/models/WorkflowVersion.ts
index 17fe045796e7f614c3bf96d67a4fcff004bf4b4e..563cdc6e4db57e903736c8896b28ca734511f12d 100644
--- a/src/client/workflow/models/WorkflowVersionReduced.ts
+++ b/src/client/workflow/models/WorkflowVersion.ts
@@ -1,14 +1,19 @@
+/* generated using openapi-typescript-codegen -- do no edit */
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
 
 import type { Status } from './Status';
 
-export type WorkflowVersionReduced = {
+export type WorkflowVersion = {
     /**
      * Status of the workflow version
      */
     status: Status;
+    /**
+     * ID of the corresponding workflow
+     */
+    workflow_id: string;
     /**
      * Version of the Workflow. Should follow semantic versioning
      */
@@ -20,10 +25,14 @@ export type WorkflowVersionReduced = {
     /**
      * URL of the icon for this workflow version
      */
-    icon_url?: string;
+    icon_url: (string | null);
+    /**
+     * Timestamp when the version was created as UNIX timestamp
+     */
+    created_at: number;
     /**
-     * Timestamp when the version was created
+     * Optional modes his workflow version has
      */
-    created_at: string;
+    modes: (Array<string> | null);
 };
 
diff --git a/src/client/workflow/models/WorkflowVersionFull.ts b/src/client/workflow/models/WorkflowVersionFull.ts
deleted file mode 100644
index 1cbe2d9831997f77f3ef5b22903d72fa58586968..0000000000000000000000000000000000000000
--- a/src/client/workflow/models/WorkflowVersionFull.ts
+++ /dev/null
@@ -1,53 +0,0 @@
-/* istanbul ignore file */
-/* tslint:disable */
-/* eslint-disable */
-
-import type { Status } from './Status';
-
-export type WorkflowVersionFull = {
-    /**
-     * Status of the workflow version
-     */
-    status: Status;
-    /**
-     * Version of the Workflow. Should follow semantic versioning
-     */
-    version: string;
-    /**
-     * Hash of the git commit
-     */
-    git_commit_hash: string;
-    /**
-     * URL of the icon for this workflow version
-     */
-    icon_url?: string;
-    /**
-     * Timestamp when the version was created
-     */
-    created_at: string;
-    /**
-     * ID of the corresponding workflow
-     */
-    workflow_id: string;
-    /**
-     * URL to download README.md from
-     */
-    readme_url: string;
-    /**
-     * URL to download CHANGELOG.md from
-     */
-    changelog_url: string;
-    /**
-     * URL to download usage.md from
-     */
-    usage_url: string;
-    /**
-     * URL to download output.md from
-     */
-    output_url: string;
-    /**
-     * URL to download nextflow_schema.json from
-     */
-    parameter_schema_url: string;
-};
-
diff --git a/src/client/workflow/models/WorkflowVersionStatus.ts b/src/client/workflow/models/WorkflowVersionStatus.ts
index 70c66d12d5ffa7a982266597d9e8e772403e0e21..0c66d1dd6b28229891c5c7d5ceed3eefab291c4e 100644
--- a/src/client/workflow/models/WorkflowVersionStatus.ts
+++ b/src/client/workflow/models/WorkflowVersionStatus.ts
@@ -1,3 +1,4 @@
+/* generated using openapi-typescript-codegen -- do no edit */
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
diff --git a/src/client/workflow/services/WorkflowCredentialsService.ts b/src/client/workflow/services/WorkflowCredentialsService.ts
new file mode 100644
index 0000000000000000000000000000000000000000..1f9f891efb06fa6cd1b4304e817b59ef954d6cd7
--- /dev/null
+++ b/src/client/workflow/services/WorkflowCredentialsService.ts
@@ -0,0 +1,99 @@
+/* generated using openapi-typescript-codegen -- do no edit */
+/* istanbul ignore file */
+/* tslint:disable */
+/* eslint-disable */
+import type { WorkflowCredentialsIn } from '../models/WorkflowCredentialsIn';
+import type { WorkflowCredentialsOut } from '../models/WorkflowCredentialsOut';
+
+import type { CancelablePromise } from '../core/CancelablePromise';
+import { OpenAPI } from '../core/OpenAPI';
+import { request as __request } from '../core/request';
+
+export class WorkflowCredentialsService {
+
+    /**
+     * Get the credentials of a workflow
+     * Get the credentials for the repository of a workflow. Only the developer of a workflow can do this.
+     *
+     * Permission "workflow:update" required.
+     * @param wid ID of a workflow
+     * @returns WorkflowCredentialsOut Successful Response
+     * @throws ApiError
+     */
+    public static workflowCredentialsGetWorkflowCredentials(
+        wid: string,
+    ): CancelablePromise<WorkflowCredentialsOut> {
+        return __request(OpenAPI, {
+            method: 'GET',
+            url: '/workflows/{wid}/credentials',
+            path: {
+                'wid': wid,
+            },
+            errors: {
+                400: `Error decoding JWT Token`,
+                403: `Not authenticated`,
+                404: `Entity not Found`,
+                422: `Validation Error`,
+            },
+        });
+    }
+
+    /**
+     * Update the credentials of a workflow
+     * Update the credentials for the repository of a workflow.
+     *
+     * Permission "workflow:update" required.
+     * @param wid ID of a workflow
+     * @param requestBody
+     * @returns any Successful Response
+     * @throws ApiError
+     */
+    public static workflowCredentialsUpdateWorkflowCredentials(
+        wid: string,
+        requestBody: WorkflowCredentialsIn,
+    ): CancelablePromise<any> {
+        return __request(OpenAPI, {
+            method: 'PUT',
+            url: '/workflows/{wid}/credentials',
+            path: {
+                'wid': wid,
+            },
+            body: requestBody,
+            mediaType: 'application/json',
+            errors: {
+                400: `Error decoding JWT Token`,
+                403: `Not authenticated`,
+                404: `Entity not Found`,
+                422: `Validation Error`,
+            },
+        });
+    }
+
+    /**
+     * Delete the credentials of a workflow
+     * Delete the credentials for the repository of a workflow.
+     *
+     * Permission "workflow:delete" required.
+     * @param wid ID of a workflow
+     * @returns void
+     * @throws ApiError
+     */
+    public static workflowCredentialsDeleteWorkflowCredentials(
+        wid: string,
+    ): CancelablePromise<void> {
+        return __request(OpenAPI, {
+            method: 'DELETE',
+            url: '/workflows/{wid}/credentials',
+            path: {
+                'wid': wid,
+            },
+            errors: {
+                400: `Error decoding JWT Token`,
+                403: `Not authenticated`,
+                404: `Entity not Found`,
+                422: `Validation Error`,
+            },
+        });
+    }
+
+}
diff --git a/src/client/workflow/services/WorkflowExecutionService.ts b/src/client/workflow/services/WorkflowExecutionService.ts
index bf28bdbd3134a94f11c2f4ede505b45be09f3674..199ba7b7ac8000a30397d7136a0cc7bec8056c1d 100644
--- a/src/client/workflow/services/WorkflowExecutionService.ts
+++ b/src/client/workflow/services/WorkflowExecutionService.ts
@@ -1,3 +1,4 @@
+/* generated using openapi-typescript-codegen -- do no edit */
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
@@ -13,30 +14,23 @@ import { request as __request } from '../core/request';
 export class WorkflowExecutionService {
 
     /**
-     * Get all workflow executions
-     * Get all workflow executions.
+     * Start a new workflow execution
+     * Start a new workflow execution. Workflow versions wit status `DEPRECATED` or `DENIED` can't be started.
      *
-     * Permission "workflow_execution:list" required, if 'user_id' is the same as the current user,
-     * otherwise "workflow_execution:list_all" required.
-     * @param userId Filter for workflow executions by a user. If none, Permission 'workflow_execution:read_any' required.
-     * @param executionStatus Filter for status of workflow execution
-     * @param workflowVersionId Filter for workflow version
+     * Permission "workflow_execution:start" required if workflow versions status is `PUBLISHED`,
+     * otherwise "workflow_execution:start_unpublished" required.
+     * @param requestBody
      * @returns WorkflowExecutionOut Successful Response
      * @throws ApiError
      */
-    public static workflowExecutionListWorkflowExecutions(
-        userId?: string,
-        executionStatus?: Array<WorkflowExecutionStatus>,
-        workflowVersionId?: string,
-    ): CancelablePromise<Array<WorkflowExecutionOut>> {
+    public static workflowExecutionStartWorkflow(
+        requestBody: WorkflowExecutionIn,
+    ): CancelablePromise<WorkflowExecutionOut> {
         return __request(OpenAPI, {
-            method: 'GET',
+            method: 'POST',
             url: '/workflow_executions',
-            query: {
-                'user_id': userId,
-                'execution_status': executionStatus,
-                'workflow_version_id': workflowVersionId,
-            },
+            body: requestBody,
+            mediaType: 'application/json',
             errors: {
                 400: `Error decoding JWT Token`,
                 403: `Not authenticated`,
@@ -47,23 +41,30 @@ export class WorkflowExecutionService {
     }
 
     /**
-     * Start a new workflow execution
-     * Start a new workflow execution. Workflow versions wit status `DEPRECATED` or `DENIED` can't be started.
+     * Get all workflow executions
+     * Get all workflow executions.
      *
-     * Permission "workflow_execution:start" required if workflow versions status is `PUBLISHED`,
-     * otherwise "workflow_execution:start_unpublished" required.
-     * @param requestBody
+     * Permission "workflow_execution:list" required, if 'user_id' is the same as the current user,
+     * otherwise "workflow_execution:list_all" required.
+     * @param userId Filter for workflow executions by a user. If none, Permission 'workflow_execution:read_any' required.
+     * @param executionStatus Filter for status of workflow execution
+     * @param workflowVersionId Filter for workflow version
      * @returns WorkflowExecutionOut Successful Response
      * @throws ApiError
      */
-    public static workflowExecutionStartWorkflow(
-        requestBody: WorkflowExecutionIn,
-    ): CancelablePromise<WorkflowExecutionOut> {
+    public static workflowExecutionListWorkflowExecutions(
+        userId?: (string | null),
+        executionStatus?: (Array<WorkflowExecutionStatus> | null),
+        workflowVersionId?: (string | null),
+    ): CancelablePromise<Array<WorkflowExecutionOut>> {
         return __request(OpenAPI, {
-            method: 'POST',
+            method: 'GET',
             url: '/workflow_executions',
-            body: requestBody,
-            mediaType: 'application/json',
+            query: {
+                'user_id': userId,
+                'execution_status': executionStatus,
+                'workflow_version_id': workflowVersionId,
+            },
             errors: {
                 400: `Error decoding JWT Token`,
                 403: `Not authenticated`,
@@ -78,6 +79,10 @@ export class WorkflowExecutionService {
      * Start a new workflow execution from an arbitrary git repository.
      *
      * Permission "workflow:create" required.
+     *
+     * For private Gitlab repositories, a Project Access Token with the role Reporter and scope `read_api` is needed.
+     *
+     * For private GitHub repositories, a Personal Access Token (classic) with scope `repo` is needed.
      * @param requestBody
      * @returns WorkflowExecutionOut Successful Response
      * @throws ApiError
@@ -155,6 +160,34 @@ export class WorkflowExecutionService {
         });
     }
 
+    /**
+     * Get the parameters of a workflow execution
+     * Get the parameters of a specific workflow execution.
+     *
+     * Permission "workflow_execution:read" required if the current user started the workflow execution,
+     * otherwise "workflow_execution:read_any" required.
+     * @param eid ID of a workflow execution.
+     * @returns any Successful Response
+     * @throws ApiError
+     */
+    public static workflowExecutionGetWorkflowExecutionParams(
+        eid: string,
+    ): CancelablePromise<Record<string, any>> {
+        return __request(OpenAPI, {
+            method: 'GET',
+            url: '/workflow_executions/{eid}/params',
+            path: {
+                'eid': eid,
+            },
+            errors: {
+                400: `Error decoding JWT Token`,
+                403: `Not authenticated`,
+                404: `Entity not Found`,
+                422: `Validation Error`,
+            },
+        });
+    }
+
     /**
      * Cancel a workflow execution
      * Cancel a running workflow execution.
diff --git a/src/client/workflow/services/WorkflowModeService.ts b/src/client/workflow/services/WorkflowModeService.ts
new file mode 100644
index 0000000000000000000000000000000000000000..4c9757918efd364406294002c88e7a1eecc2c1ad
--- /dev/null
+++ b/src/client/workflow/services/WorkflowModeService.ts
@@ -0,0 +1,40 @@
+/* generated using openapi-typescript-codegen -- do no edit */
+/* istanbul ignore file */
+/* tslint:disable */
+/* eslint-disable */
+import type { WorkflowModeOut } from '../models/WorkflowModeOut';
+
+import type { CancelablePromise } from '../core/CancelablePromise';
+import { OpenAPI } from '../core/OpenAPI';
+import { request as __request } from '../core/request';
+
+export class WorkflowModeService {
+
+    /**
+     * List workflows
+     * Get a workflow mode
+     *
+     * Permission 'workflow:read' required
+     * @param modeId ID of a workflow mode
+     * @returns WorkflowModeOut Successful Response
+     * @throws ApiError
+     */
+    public static workflowModeGetWorkflowMode(
+        modeId: string,
+    ): CancelablePromise<WorkflowModeOut> {
+        return __request(OpenAPI, {
+            method: 'GET',
+            url: '/workflow_modes/{mode_id}',
+            path: {
+                'mode_id': modeId,
+            },
+            errors: {
+                400: `Error decoding JWT Token`,
+                403: `Not authenticated`,
+                404: `Entity not Found`,
+                422: `Validation Error`,
+            },
+        });
+    }
+
+}
diff --git a/src/client/workflow/services/WorkflowService.ts b/src/client/workflow/services/WorkflowService.ts
index 45f9ffa369494568f828b205f6c156855d2096e4..dad2fcb71c8296bc1ca20c2989db666d735a599a 100644
--- a/src/client/workflow/services/WorkflowService.ts
+++ b/src/client/workflow/services/WorkflowService.ts
@@ -1,12 +1,16 @@
+/* generated using openapi-typescript-codegen -- do no edit */
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
-import type { Body_Workflow_create_workflow } from '../models/Body_Workflow_create_workflow';
-import type { Body_Workflow_update_workflow } from '../models/Body_Workflow_update_workflow';
+import type { Body_Workflow_Version_upload_workflow_version_icon } from '../models/Body_Workflow_Version_upload_workflow_version_icon';
+import type { DocumentationEnum } from '../models/DocumentationEnum';
+import type { IconUpdateOut } from '../models/IconUpdateOut';
 import type { Status } from '../models/Status';
+import type { WorkflowIn } from '../models/WorkflowIn';
 import type { WorkflowOut } from '../models/WorkflowOut';
 import type { WorkflowStatistic } from '../models/WorkflowStatistic';
-import type { WorkflowVersionFull } from '../models/WorkflowVersionFull';
+import type { WorkflowUpdate } from '../models/WorkflowUpdate';
+import type { WorkflowVersion } from '../models/WorkflowVersion';
 import type { WorkflowVersionStatus } from '../models/WorkflowVersionStatus';
 
 import type { CancelablePromise } from '../core/CancelablePromise';
@@ -27,9 +31,9 @@ export class WorkflowService {
      * @throws ApiError
      */
     public static workflowListWorkflows(
-        nameSubstring?: string,
-        versionStatus?: Array<Status>,
-        developerId?: string,
+        nameSubstring?: (string | null),
+        versionStatus?: (Array<Status> | null),
+        developerId?: (string | null),
     ): CancelablePromise<Array<WorkflowOut>> {
         return __request(OpenAPI, {
             method: 'GET',
@@ -51,20 +55,24 @@ export class WorkflowService {
     /**
      * Create a new workflow
      * Create a new workflow.
-     * R
+     *
      * Permission "workflow:create" required.
-     * @param formData
+     *
+     * For private Gitlab repositories, a Project Access Token with the role Reporter and scope `read_api` is needed.
+     *
+     * For private GitHub repositories, a Personal Access Token (classic) with scope `repo` is needed.
+     * @param requestBody
      * @returns WorkflowOut Successful Response
      * @throws ApiError
      */
     public static workflowCreateWorkflow(
-        formData: Body_Workflow_create_workflow,
+        requestBody: WorkflowIn,
     ): CancelablePromise<WorkflowOut> {
         return __request(OpenAPI, {
             method: 'POST',
             url: '/workflows',
-            formData: formData,
-            mediaType: 'multipart/form-data',
+            body: requestBody,
+            mediaType: 'application/json',
             errors: {
                 400: `Error decoding JWT Token`,
                 403: `Not authenticated`,
@@ -86,7 +94,7 @@ export class WorkflowService {
      */
     public static workflowGetWorkflow(
         wid: string,
-        versionStatus?: Array<Status>,
+        versionStatus?: (Array<Status> | null),
     ): CancelablePromise<WorkflowOut> {
         return __request(OpenAPI, {
             method: 'GET',
@@ -164,22 +172,22 @@ export class WorkflowService {
      *
      * Permission "workflow:update" required.
      * @param wid ID of a workflow
-     * @param formData
-     * @returns WorkflowVersionFull Successful Response
+     * @param requestBody
+     * @returns WorkflowVersion Successful Response
      * @throws ApiError
      */
     public static workflowUpdateWorkflow(
         wid: string,
-        formData: Body_Workflow_update_workflow,
-    ): CancelablePromise<WorkflowVersionFull> {
+        requestBody: WorkflowUpdate,
+    ): CancelablePromise<WorkflowVersion> {
         return __request(OpenAPI, {
             method: 'POST',
             url: '/workflows/{wid}/update',
             path: {
                 'wid': wid,
             },
-            formData: formData,
-            mediaType: 'multipart/form-data',
+            body: requestBody,
+            mediaType: 'application/json',
             errors: {
                 400: `Error decoding JWT Token`,
                 403: `Not authenticated`,
@@ -196,13 +204,13 @@ export class WorkflowService {
      * Permission "workflow:list" required.
      * @param wid ID of a workflow
      * @param versionStatus Which versions of the workflow to include in the response. Permission 'workflow:list_filter' required if you are not the developer of this workflow. Default PUBLISHED and DEPRECATED
-     * @returns WorkflowVersionFull Successful Response
+     * @returns WorkflowVersion Successful Response
      * @throws ApiError
      */
     public static workflowVersionListWorkflowVersion(
         wid: string,
-        versionStatus?: Array<Status>,
-    ): CancelablePromise<Array<WorkflowVersionFull>> {
+        versionStatus?: (Array<Status> | null),
+    ): CancelablePromise<Array<WorkflowVersion>> {
         return __request(OpenAPI, {
             method: 'GET',
             url: '/workflows/{wid}/versions',
@@ -229,13 +237,13 @@ export class WorkflowService {
      * otherwise "workflow:read_any"
      * @param gitCommitHash Git commit git_commit_hash of specific version or 'latest'.
      * @param wid ID of a workflow
-     * @returns WorkflowVersionFull Successful Response
+     * @returns WorkflowVersion Successful Response
      * @throws ApiError
      */
     public static workflowVersionGetWorkflowVersion(
         gitCommitHash: string,
         wid: string,
-    ): CancelablePromise<WorkflowVersionFull> {
+    ): CancelablePromise<WorkflowVersion> {
         return __request(OpenAPI, {
             method: 'GET',
             url: '/workflows/{wid}/versions/{git_commit_hash}',
@@ -260,14 +268,14 @@ export class WorkflowService {
      * @param gitCommitHash Git commit git_commit_hash of specific version.
      * @param wid ID of a workflow
      * @param requestBody
-     * @returns WorkflowVersionFull Successful Response
+     * @returns WorkflowVersion Successful Response
      * @throws ApiError
      */
     public static workflowVersionUpdateWorkflowVersionStatus(
         gitCommitHash: string,
         wid: string,
         requestBody: WorkflowVersionStatus,
-    ): CancelablePromise<WorkflowVersionFull> {
+    ): CancelablePromise<WorkflowVersion> {
         return __request(OpenAPI, {
             method: 'PATCH',
             url: '/workflows/{wid}/versions/{git_commit_hash}/status',
@@ -291,22 +299,125 @@ export class WorkflowService {
      * Deprecate a workflow version.
      *
      * Permission "workflow:update" required if you are the developer of the workflow,
-     * otherwise "workflow:read_status""
-     * @param gitCommitHash Git commit git_commit_hash of specific version.
+     * otherwise "workflow:read_status"
      * @param wid ID of a workflow
-     * @returns WorkflowVersionFull Successful Response
+     * @param gitCommitHash Git commit git_commit_hash of specific version.
+     * @returns WorkflowVersion Successful Response
      * @throws ApiError
      */
     public static workflowVersionDeprecateWorkflowVersion(
-        gitCommitHash: string,
         wid: string,
-    ): CancelablePromise<WorkflowVersionFull> {
+        gitCommitHash: string,
+    ): CancelablePromise<WorkflowVersion> {
         return __request(OpenAPI, {
             method: 'POST',
             url: '/workflows/{wid}/versions/{git_commit_hash}/deprecate',
             path: {
+                'wid': wid,
                 'git_commit_hash': gitCommitHash,
+            },
+            errors: {
+                400: `Error decoding JWT Token`,
+                403: `Not authenticated`,
+                404: `Entity not Found`,
+                422: `Validation Error`,
+            },
+        });
+    }
+
+    /**
+     * Fetch documentation for a workflow version
+     * Get the documentation for a specific workflow version.
+     * Streams the response directly from the right git repository.
+     *
+     * Permission "workflow:read" required.
+     * @param wid ID of a workflow
+     * @param gitCommitHash Git commit git_commit_hash of specific version.
+     * @param document Specific which type of documentation the client wants to fetch
+     * @param modeId Workflow Mode
+     * @returns any Successful Response
+     * @throws ApiError
+     */
+    public static workflowVersionDownloadWorkflowDocumentation(
+        wid: string,
+        gitCommitHash: string,
+        document?: DocumentationEnum,
+        modeId?: (string | null),
+    ): CancelablePromise<any> {
+        return __request(OpenAPI, {
+            method: 'GET',
+            url: '/workflows/{wid}/versions/{git_commit_hash}/documentation',
+            path: {
                 'wid': wid,
+                'git_commit_hash': gitCommitHash,
+            },
+            query: {
+                'document': document,
+                'mode_id': modeId,
+            },
+            errors: {
+                400: `Error decoding JWT Token`,
+                403: `Not authenticated`,
+                404: `Entity not Found`,
+                422: `Validation Error`,
+            },
+        });
+    }
+
+    /**
+     * Upload icon for workflow version
+     * Upload an icon for the workflow version and returns the new icon URL.
+     *
+     * Permission "workflow:update" required.
+     * @param wid ID of a workflow
+     * @param gitCommitHash Git commit git_commit_hash of specific version.
+     * @param formData
+     * @returns IconUpdateOut Successful Response
+     * @throws ApiError
+     */
+    public static workflowVersionUploadWorkflowVersionIcon(
+        wid: string,
+        gitCommitHash: string,
+        formData: Body_Workflow_Version_upload_workflow_version_icon,
+    ): CancelablePromise<IconUpdateOut> {
+        return __request(OpenAPI, {
+            method: 'POST',
+            url: '/workflows/{wid}/versions/{git_commit_hash}/icon',
+            path: {
+                'wid': wid,
+                'git_commit_hash': gitCommitHash,
+            },
+            formData: formData,
+            mediaType: 'multipart/form-data',
+            errors: {
+                400: `Error decoding JWT Token`,
+                403: `Not authenticated`,
+                404: `Entity not Found`,
+                422: `Validation Error`,
+            },
+        });
+    }
+
+    /**
+     * Delete icon of workflow version
+     * Delete the icon of the workflow version.
+     *
+     * Permission "workflow:update" required.
+     * @param wid ID of a workflow
+     * @param gitCommitHash Git commit git_commit_hash of specific version.
+     * @returns void
+     * @throws ApiError
+     */
+    public static workflowVersionDeleteWorkflowVersionIcon(
+        wid: string,
+        gitCommitHash: string,
+    ): CancelablePromise<void> {
+        return __request(OpenAPI, {
+            method: 'DELETE',
+            url: '/workflows/{wid}/versions/{git_commit_hash}/icon',
+            path: {
+                'wid': wid,
+                'git_commit_hash': gitCommitHash,
             },
             errors: {
                 400: `Error decoding JWT Token`,
diff --git a/src/client/workflow/services/WorkflowVersionService.ts b/src/client/workflow/services/WorkflowVersionService.ts
index 25687b7b5fc92b1cb50a952ac391398461950a42..0d46dd904a575a8b3b60d2d399b494f6dce0182a 100644
--- a/src/client/workflow/services/WorkflowVersionService.ts
+++ b/src/client/workflow/services/WorkflowVersionService.ts
@@ -1,8 +1,12 @@
+/* generated using openapi-typescript-codegen -- do no edit */
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
+import type { Body_Workflow_Version_upload_workflow_version_icon } from '../models/Body_Workflow_Version_upload_workflow_version_icon';
+import type { DocumentationEnum } from '../models/DocumentationEnum';
+import type { IconUpdateOut } from '../models/IconUpdateOut';
 import type { Status } from '../models/Status';
-import type { WorkflowVersionFull } from '../models/WorkflowVersionFull';
+import type { WorkflowVersion } from '../models/WorkflowVersion';
 import type { WorkflowVersionStatus } from '../models/WorkflowVersionStatus';
 
 import type { CancelablePromise } from '../core/CancelablePromise';
@@ -18,13 +22,13 @@ export class WorkflowVersionService {
      * Permission "workflow:list" required.
      * @param wid ID of a workflow
      * @param versionStatus Which versions of the workflow to include in the response. Permission 'workflow:list_filter' required if you are not the developer of this workflow. Default PUBLISHED and DEPRECATED
-     * @returns WorkflowVersionFull Successful Response
+     * @returns WorkflowVersion Successful Response
      * @throws ApiError
      */
     public static workflowVersionListWorkflowVersion(
         wid: string,
-        versionStatus?: Array<Status>,
-    ): CancelablePromise<Array<WorkflowVersionFull>> {
+        versionStatus?: (Array<Status> | null),
+    ): CancelablePromise<Array<WorkflowVersion>> {
         return __request(OpenAPI, {
             method: 'GET',
             url: '/workflows/{wid}/versions',
@@ -51,13 +55,13 @@ export class WorkflowVersionService {
      * otherwise "workflow:read_any"
      * @param gitCommitHash Git commit git_commit_hash of specific version or 'latest'.
      * @param wid ID of a workflow
-     * @returns WorkflowVersionFull Successful Response
+     * @returns WorkflowVersion Successful Response
      * @throws ApiError
      */
     public static workflowVersionGetWorkflowVersion(
         gitCommitHash: string,
         wid: string,
-    ): CancelablePromise<WorkflowVersionFull> {
+    ): CancelablePromise<WorkflowVersion> {
         return __request(OpenAPI, {
             method: 'GET',
             url: '/workflows/{wid}/versions/{git_commit_hash}',
@@ -82,14 +86,14 @@ export class WorkflowVersionService {
      * @param gitCommitHash Git commit git_commit_hash of specific version.
      * @param wid ID of a workflow
      * @param requestBody
-     * @returns WorkflowVersionFull Successful Response
+     * @returns WorkflowVersion Successful Response
      * @throws ApiError
      */
     public static workflowVersionUpdateWorkflowVersionStatus(
         gitCommitHash: string,
         wid: string,
         requestBody: WorkflowVersionStatus,
-    ): CancelablePromise<WorkflowVersionFull> {
+    ): CancelablePromise<WorkflowVersion> {
         return __request(OpenAPI, {
             method: 'PATCH',
             url: '/workflows/{wid}/versions/{git_commit_hash}/status',
@@ -113,22 +117,125 @@ export class WorkflowVersionService {
      * Deprecate a workflow version.
      *
      * Permission "workflow:update" required if you are the developer of the workflow,
-     * otherwise "workflow:read_status""
-     * @param gitCommitHash Git commit git_commit_hash of specific version.
+     * otherwise "workflow:read_status"
      * @param wid ID of a workflow
-     * @returns WorkflowVersionFull Successful Response
+     * @param gitCommitHash Git commit git_commit_hash of specific version.
+     * @returns WorkflowVersion Successful Response
      * @throws ApiError
      */
     public static workflowVersionDeprecateWorkflowVersion(
-        gitCommitHash: string,
         wid: string,
-    ): CancelablePromise<WorkflowVersionFull> {
+        gitCommitHash: string,
+    ): CancelablePromise<WorkflowVersion> {
         return __request(OpenAPI, {
             method: 'POST',
             url: '/workflows/{wid}/versions/{git_commit_hash}/deprecate',
             path: {
+                'wid': wid,
+                'git_commit_hash': gitCommitHash,
+            },
+            errors: {
+                400: `Error decoding JWT Token`,
+                403: `Not authenticated`,
+                404: `Entity not Found`,
+                422: `Validation Error`,
+            },
+        });
+    }
+
+    /**
+     * Fetch documentation for a workflow version
+     * Get the documentation for a specific workflow version.
+     * Streams the response directly from the right git repository.
+     *
+     * Permission "workflow:read" required.
+     * @param wid ID of a workflow
+     * @param gitCommitHash Git commit git_commit_hash of specific version.
+     * @param document Specific which type of documentation the client wants to fetch
+     * @param modeId Workflow Mode
+     * @returns any Successful Response
+     * @throws ApiError
+     */
+    public static workflowVersionDownloadWorkflowDocumentation(
+        wid: string,
+        gitCommitHash: string,
+        document?: DocumentationEnum,
+        modeId?: (string | null),
+    ): CancelablePromise<any> {
+        return __request(OpenAPI, {
+            method: 'GET',
+            url: '/workflows/{wid}/versions/{git_commit_hash}/documentation',
+            path: {
+                'wid': wid,
+                'git_commit_hash': gitCommitHash,
+            },
+            query: {
+                'document': document,
+                'mode_id': modeId,
+            },
+            errors: {
+                400: `Error decoding JWT Token`,
+                403: `Not authenticated`,
+                404: `Entity not Found`,
+                422: `Validation Error`,
+            },
+        });
+    }
+
+    /**
+     * Upload icon for workflow version
+     * Upload an icon for the workflow version and returns the new icon URL.
+     *
+     * Permission "workflow:update" required.
+     * @param wid ID of a workflow
+     * @param gitCommitHash Git commit git_commit_hash of specific version.
+     * @param formData
+     * @returns IconUpdateOut Successful Response
+     * @throws ApiError
+     */
+    public static workflowVersionUploadWorkflowVersionIcon(
+        wid: string,
+        gitCommitHash: string,
+        formData: Body_Workflow_Version_upload_workflow_version_icon,
+    ): CancelablePromise<IconUpdateOut> {
+        return __request(OpenAPI, {
+            method: 'POST',
+            url: '/workflows/{wid}/versions/{git_commit_hash}/icon',
+            path: {
+                'wid': wid,
                 'git_commit_hash': gitCommitHash,
+            },
+            formData: formData,
+            mediaType: 'multipart/form-data',
+            errors: {
+                400: `Error decoding JWT Token`,
+                403: `Not authenticated`,
+                404: `Entity not Found`,
+                422: `Validation Error`,
+            },
+        });
+    }
+
+    /**
+     * Delete icon of workflow version
+     * Delete the icon of the workflow version.
+     *
+     * Permission "workflow:update" required.
+     * @param wid ID of a workflow
+     * @param gitCommitHash Git commit git_commit_hash of specific version.
+     * @returns void
+     * @throws ApiError
+     */
+    public static workflowVersionDeleteWorkflowVersionIcon(
+        wid: string,
+        gitCommitHash: string,
+    ): CancelablePromise<void> {
+        return __request(OpenAPI, {
+            method: 'DELETE',
+            url: '/workflows/{wid}/versions/{git_commit_hash}/icon',
+            path: {
                 'wid': wid,
+                'git_commit_hash': gitCommitHash,
             },
             errors: {
                 400: `Error decoding JWT Token`,
diff --git a/src/components/NavbarTop.vue b/src/components/NavbarTop.vue
index 192d93f679bd7146de30821b9deea9fcdeaf425c..2c08939a0e2769614f48e6c99a829fa8469d8094 100644
--- a/src/components/NavbarTop.vue
+++ b/src/components/NavbarTop.vue
@@ -21,10 +21,10 @@ function logout() {
 
 const activeRoute = ref("");
 const objectStorageActive = computed<boolean>(
-  () => activeRoute.value == "buckets" || activeRoute.value == "s3_keys"
+  () => activeRoute.value == "buckets" || activeRoute.value == "s3_keys",
 );
 const workflowActive = computed<boolean>(
-  () => activeRoute.value == "workflows"
+  () => activeRoute.value == "workflows",
 );
 
 watch(
@@ -41,7 +41,7 @@ watch(
     } else {
       activeRoute.value = "";
     }
-  }
+  },
 );
 </script>
 
diff --git a/src/components/modals/SearchUserModal.vue b/src/components/modals/SearchUserModal.vue
index e7ce11b160cf6ef6c5ba935f07b41b7e63d957ac..990cfd8256b31a3ec5f48b0ff131fe1f7be769fd 100644
--- a/src/components/modals/SearchUserModal.vue
+++ b/src/components/modals/SearchUserModal.vue
@@ -41,7 +41,7 @@ watch(
       formState.loading = false;
       formState.potentialUsers = [];
     }
-  }
+  },
 );
 
 const emit = defineEmits<{
@@ -58,7 +58,7 @@ function searchUser(name: string) {
   UserService.userListUsers(name)
     .then((userSuggestions) => {
       formState.potentialUsers = userSuggestions.filter(
-        (user) => store.currentUID != user.uid
+        (user) => store.currentUID != user.uid,
       );
     })
     .catch((err) => {
diff --git a/src/components/object-storage/BucketListItem.vue b/src/components/object-storage/BucketListItem.vue
index 889466b74a81c1e4c0149852597ee33f8572b853..bd333f7345769d89b4b88b0d53bd02825589922c 100644
--- a/src/components/object-storage/BucketListItem.vue
+++ b/src/components/object-storage/BucketListItem.vue
@@ -26,14 +26,9 @@ const permissionRepository = useBucketStore();
 const router = useRouter();
 
 const permission = computed<BucketPermissionOut | undefined>(() =>
-  permissionRepository.getBucketPermission(props.bucket.name)
+  permissionRepository.getBucketPermission(props.bucket.name),
 );
 
-const bucketNameWithoutTenant = computed<string>(() => {
-  const splittedBucketName = props.bucket.name.split(":");
-  return splittedBucketName[splittedBucketName.length - 1];
-});
-
 const emit = defineEmits<{
   (e: "delete-bucket", bucketName: string): void;
 }>();
@@ -91,9 +86,7 @@ onMounted(() => {
           params: { bucketName: bucket.name, subFolders: [] },
         }"
       >
-        <span class="text-truncate" style="width: 80%">{{
-          bucketNameWithoutTenant
-        }}</span>
+        <span class="text-truncate" style="width: 80%">{{ bucket.name }}</span>
         <div>
           <font-awesome-icon
             v-if="props.active && !permission && props.deletable"
@@ -135,10 +128,10 @@ onMounted(() => {
                   :id="'tooltip-' + randomIDSuffix"
                   data-bs-toggle="tooltip"
                   :data-bs-title="
-                    dayjs(bucket.created_at).format('DD.MM.YYYY HH:mm:ss')
+                    dayjs.unix(bucket.created_at).format('DD.MM.YYYY HH:mm:ss')
                   "
                 >
-                  {{ dayjs(bucket.created_at).fromNow() }}
+                  {{ dayjs.unix(bucket.created_at).fromNow() }}
                 </span>
               </td>
             </tr>
@@ -148,7 +141,9 @@ onMounted(() => {
             </tr>
             <tr>
               <th scope="row" class="fw-bold">Size:</th>
-              <td>{{ filesize(bucket.size) }}</td>
+              <td>
+                {{ filesize(bucket.size, { base: 2, standard: "jedec" }) }}
+              </td>
             </tr>
           </tbody>
         </table>
diff --git a/src/components/object-storage/modals/BucketDetailModal.vue b/src/components/object-storage/modals/BucketDetailModal.vue
index 5fbff6fdd8269d2f5e8b655cba4afc60e093812c..3ee95a711befbbc7541e4764989ff6580e8a5023 100644
--- a/src/components/object-storage/modals/BucketDetailModal.vue
+++ b/src/components/object-storage/modals/BucketDetailModal.vue
@@ -3,17 +3,11 @@ import BootstrapModal from "@/components/modals/BootstrapModal.vue";
 import type { BucketOut } from "@/client/s3proxy";
 import dayjs from "dayjs";
 import { filesize } from "filesize";
-import { computed } from "vue";
 
 const props = defineProps<{
   modalID: string;
   bucket: BucketOut;
 }>();
-
-const bucketNameWithoutTenant = computed<string>(() => {
-  const splittedBucketName = props.bucket.name.split(":");
-  return splittedBucketName[splittedBucketName.length - 1];
-});
 </script>
 
 <template>
@@ -34,13 +28,15 @@ const bucketNameWithoutTenant = computed<string>(() => {
           <tbody>
             <tr>
               <th scope="row" class="col-2">Name</th>
-              <td class="col-10">{{ bucketNameWithoutTenant }}</td>
+              <td class="col-10">{{ props.bucket.name }}</td>
             </tr>
             <tr>
               <th scope="row">Creation date</th>
               <td>
                 {{
-                  dayjs(props.bucket.created_at).format("YYYY-MM-DD HH:mm:ss")
+                  dayjs
+                    .unix(props.bucket.created_at)
+                    .format("YYYY-MM-DD HH:mm:ss")
                 }}
               </td>
             </tr>
@@ -50,7 +46,11 @@ const bucketNameWithoutTenant = computed<string>(() => {
             </tr>
             <tr>
               <th scope="row">Size</th>
-              <td>{{ filesize(props.bucket.size) }}</td>
+              <td>
+                {{
+                  filesize(props.bucket.size, { base: 2, standard: "jedec" })
+                }}
+              </td>
             </tr>
             <tr>
               <th scope="row">Description</th>
diff --git a/src/components/object-storage/modals/CopyObjectModal.vue b/src/components/object-storage/modals/CopyObjectModal.vue
index 010da42b3633b5e0df21136365e73e5c14455865..32313142e8a8b9acfaadebd1353f0b67fa12c124 100644
--- a/src/components/object-storage/modals/CopyObjectModal.vue
+++ b/src/components/object-storage/modals/CopyObjectModal.vue
@@ -39,16 +39,11 @@ function getFileName(key: string): string {
   return splittedKey[splittedKey.length - 1];
 }
 
-function bucketNameWithoutTenant(name: string): string {
-  const splittedBucketName = name.split(":");
-  return splittedBucketName[splittedBucketName.length - 1];
-}
-
 function copyObject() {
   const command = new CopyObjectCommand({
     Bucket: formState.destBucket,
     CopySource: encodeURI(
-      `/${props.sourceObject.bucket}/${props.sourceObject.key}`
+      `/${props.sourceObject.bucket}/${props.sourceObject.key}`,
     ),
     Key: formState.destKey,
   });
@@ -84,7 +79,7 @@ watch(
   () => props.sourceObject.key,
   (newKey) => {
     formState.destKey = newKey;
-  }
+  },
 );
 
 onMounted(() => {
@@ -174,7 +169,7 @@ onMounted(() => {
                   :key="bucket.name"
                   :value="bucket.name"
                 >
-                  {{ bucketNameWithoutTenant(bucket.name) }}
+                  {{ bucket.name }}
                 </option>
               </select>
             </div>
diff --git a/src/components/object-storage/modals/CreateBucketModal.vue b/src/components/object-storage/modals/CreateBucketModal.vue
index 69d625863ec8dc4fc61d7bca107bd85c08930dd4..94fe7288e46131437e33d981575aa6696f423f6e 100644
--- a/src/components/object-storage/modals/CreateBucketModal.vue
+++ b/src/components/object-storage/modals/CreateBucketModal.vue
@@ -61,7 +61,7 @@ function createBucket() {
       },
       () => {
         formState.loading = false;
-      }
+      },
     );
   }
 }
diff --git a/src/components/object-storage/modals/CreateFolderModal.vue b/src/components/object-storage/modals/CreateFolderModal.vue
index 8484696e7486de6fc177606d879256684fe300d8..627b3a8428c672e99c2cffeb8c9e25c7bfd51203 100644
--- a/src/components/object-storage/modals/CreateFolderModal.vue
+++ b/src/components/object-storage/modals/CreateFolderModal.vue
@@ -25,11 +25,6 @@ const emit = defineEmits<{
   (e: "folder-created", object: S3ObjectMetaInformation): void;
 }>();
 
-const bucketNameWithoutTenant = computed<string>(() => {
-  const splittedBucketName = props.bucketName.split(":");
-  return splittedBucketName[splittedBucketName.length - 1];
-});
-
 const formState = reactive<{
   folderName: string;
   uploading: boolean;
@@ -47,13 +42,11 @@ function uploadFolder() {
     .replace(/(\/)\1+/g, "/")
     .split("")
     .reverse();
-  console.log(reversedKey);
   const firstLetterIndex = reversedKey.findIndex((char) => char !== "/");
   if (firstLetterIndex < 0) {
     return;
   }
   const realKey = reversedKey.slice(firstLetterIndex).reverse().join("") + "/";
-  console.log(realKey);
   const command = new PutObjectCommand({
     Bucket: props.bucketName,
     Body: "",
@@ -143,7 +136,7 @@ onMounted(() => {
     <template v-slot:header>
       <h4>Create folder in</h4>
       <ol class="breadcrumb">
-        <li class="breadcrumb-item">{{ bucketNameWithoutTenant }}</li>
+        <li class="breadcrumb-item">{{ props.bucketName }}</li>
         <li
           class="breadcrumb-item"
           v-for="folder in currentFolders"
diff --git a/src/components/object-storage/modals/ObjectDetailModal.vue b/src/components/object-storage/modals/ObjectDetailModal.vue
index 6d929efd8f90a179c2bc7da79f990320cd6f4365..cea5b4adddf382235a706683f8bb8522770fe934 100644
--- a/src/components/object-storage/modals/ObjectDetailModal.vue
+++ b/src/components/object-storage/modals/ObjectDetailModal.vue
@@ -3,17 +3,11 @@ import BootstrapModal from "@/components/modals/BootstrapModal.vue";
 import type { S3ObjectMetaInformation } from "@/client/s3proxy";
 import dayjs from "dayjs";
 import { filesize } from "filesize";
-import { computed } from "vue";
 
 const props = defineProps<{
   modalID: string;
   s3Object: S3ObjectMetaInformation;
 }>();
-
-const bucketNameWithoutTenant = computed<string>(() => {
-  const splittedBucketName = props.s3Object.bucket.split(":");
-  return splittedBucketName[splittedBucketName.length - 1];
-});
 </script>
 
 <template>
@@ -31,7 +25,7 @@ const bucketNameWithoutTenant = computed<string>(() => {
           <tbody>
             <tr>
               <th scope="row" class="col-4">Bucket</th>
-              <td class="col-8">{{ bucketNameWithoutTenant }}</td>
+              <td class="col-8">{{ props.s3Object.bucket }}</td>
             </tr>
             <tr>
               <th scope="row">Name</th>
@@ -46,14 +40,18 @@ const bucketNameWithoutTenant = computed<string>(() => {
               <td>
                 {{
                   dayjs(props.s3Object.last_modified).format(
-                    "YYYY-MM-DD HH:mm:ss"
+                    "YYYY-MM-DD HH:mm:ss",
                   )
                 }}
               </td>
             </tr>
             <tr>
               <th scope="row">Size</th>
-              <td>{{ filesize(props.s3Object.size) }}</td>
+              <td>
+                {{
+                  filesize(props.s3Object.size, { base: 2, standard: "jedec" })
+                }}
+              </td>
             </tr>
           </tbody>
         </table>
diff --git a/src/components/object-storage/modals/PermissionListModal.vue b/src/components/object-storage/modals/PermissionListModal.vue
index e28671a591b21625c9fa45898c267442b143765e..71ad1ed8937c4165fd30a836b169921f70a359db 100644
--- a/src/components/object-storage/modals/PermissionListModal.vue
+++ b/src/components/object-storage/modals/PermissionListModal.vue
@@ -30,6 +30,9 @@ const state = reactive<{
     uid: "uid",
     permission: "READ",
     grantee_display_name: "display_name",
+    from_timestamp: null,
+    to_timestamp: null,
+    file_prefix: null,
   },
 });
 
@@ -41,7 +44,7 @@ watch(
   () => props.bucketName,
   (newBucketName) => {
     updateBucketPermissions(newBucketName);
-  }
+  },
 );
 
 watch(
@@ -50,7 +53,7 @@ watch(
     if (newBucketPermission !== undefined) {
       state.permissions.push(newBucketPermission);
     }
-  }
+  },
 );
 
 // Function
@@ -71,7 +74,7 @@ function updateBucketPermissions(bucketName: string) {
 
 function permissionDeleted(bucketPermission: BucketPermissionIn) {
   state.permissions = state.permissions.filter(
-    (perm) => perm.uid != bucketPermission.uid
+    (perm) => perm.uid != bucketPermission.uid,
   );
 }
 
@@ -81,7 +84,7 @@ function permissionCreated(bucketPermission: BucketPermissionOut) {
 
 function permissionEdited(bucketPermission: BucketPermissionOut) {
   const index = state.permissions.findIndex(
-    (perm) => perm.uid == bucketPermission.uid
+    (perm) => perm.uid == bucketPermission.uid,
   );
   state.permissions[index] = bucketPermission;
 }
diff --git a/src/components/object-storage/modals/PermissionModal.vue b/src/components/object-storage/modals/PermissionModal.vue
index b5e4b2353d4787c5960632ff8674839683a60073..c94f63eecd6d907645968c535dfaf8ff0970316b 100644
--- a/src/components/object-storage/modals/PermissionModal.vue
+++ b/src/components/object-storage/modals/PermissionModal.vue
@@ -66,22 +66,17 @@ const permissionForm = ref<HTMLFormElement | undefined>(undefined);
 // Computes Properties
 // -----------------------------------------------------------------------------
 const editPermission = computed<boolean>(
-  () => props.editUserPermission != undefined
+  () => props.editUserPermission != undefined,
 );
 
 const possibleSubFolders = computed<string[]>(() =>
-  findSubFolders(props.subFolders, [])
+  findSubFolders(props.subFolders, []),
 );
 
 const permissionUserReadonly = computed<boolean>(() => {
   return formState.readonly || editPermission.value;
 });
 
-const bucketNameWithoutTenant = computed<string>(() => {
-  const splittedBucketName = props.bucketName.split(":");
-  return splittedBucketName[splittedBucketName.length - 1];
-});
-
 // Watchers
 // -----------------------------------------------------------------------------
 watch(
@@ -89,12 +84,12 @@ watch(
   (newBucketName) => {
     updatePermission();
     permission.bucket_name = newBucketName;
-  }
+  },
 );
 
 watch(
   () => props.editUserPermission,
-  () => updatePermission()
+  () => updatePermission(),
 );
 
 // Events
@@ -129,7 +124,7 @@ function toastHidden() {
  * Check if an input should be visible based on its state
  * @param input Input which visibility should be determined.
  */
-function inputVisible(input?: string): boolean {
+function inputVisible(input?: number | string | null): boolean {
   return !formState.readonly || input != undefined;
 }
 
@@ -142,14 +137,8 @@ function updatePermission() {
     permission.file_prefix = props.editUserPermission.file_prefix;
     permission.uid = props.editUserPermission.uid;
     formState.grantee_name = props.editUserPermission.grantee_display_name;
-    permission.from_timestamp =
-      props.editUserPermission.from_timestamp != null
-        ? dayjs(props.editUserPermission.from_timestamp).format("YYYY-MM-DD")
-        : undefined;
-    permission.to_timestamp =
-      props.editUserPermission.to_timestamp != null
-        ? dayjs(props.editUserPermission.to_timestamp).format("YYYY-MM-DD")
-        : undefined;
+    permission.from_timestamp = props.editUserPermission.from_timestamp;
+    permission.to_timestamp = props.editUserPermission.to_timestamp;
     permission.permission = props.editUserPermission.permission;
   } else {
     permission.file_prefix = undefined;
@@ -168,7 +157,7 @@ function updatePermission() {
  */
 function findSubFolders(
   currentFolder: FolderTree,
-  parentFolders: string[]
+  parentFolders: string[],
 ): string[] {
   const arr: string[] = [];
   for (const subFolder of Object.keys(currentFolder.subFolders)) {
@@ -180,8 +169,8 @@ function findSubFolders(
       subFolderString,
       ...findSubFolders(
         currentFolder.subFolders[subFolder],
-        subFolderString.slice(0, subFolderString.length - 1).split("/")
-      )
+        subFolderString.slice(0, subFolderString.length - 1).split("/"),
+      ),
     );
   }
   return arr;
@@ -193,34 +182,19 @@ function findSubFolders(
 function formSubmit() {
   formState.error = false;
   if (permissionForm.value?.checkValidity()) {
-    const tempPermission: BucketPermissionIn = permission;
-    if (permission.from_timestamp != null) {
-      tempPermission.from_timestamp =
-        permission.from_timestamp.length > 0
-          ? dayjs(permission.from_timestamp).toISOString()
-          : undefined;
-    }
-    if (permission.to_timestamp != null) {
-      tempPermission.to_timestamp =
-        permission.to_timestamp.length > 0
-          ? dayjs(permission.to_timestamp).toISOString()
-          : undefined;
-    }
     formState.loading = true;
     const serverAnswerPromise = editPermission.value
       ? BucketPermissionService.bucketPermissionUpdatePermission(
           permission.uid,
           permission.bucket_name,
           {
-            to_timestamp: tempPermission.to_timestamp,
-            from_timestamp: tempPermission.from_timestamp,
-            permission: tempPermission.permission,
-            file_prefix: tempPermission.file_prefix,
-          } as BucketPermissionParameters
+            to_timestamp: permission.to_timestamp,
+            from_timestamp: permission.from_timestamp,
+            permission: permission.permission,
+            file_prefix: permission.file_prefix,
+          } as BucketPermissionParameters,
         )
-      : BucketPermissionService.bucketPermissionCreatePermission(
-          tempPermission
-        );
+      : BucketPermissionService.bucketPermissionCreatePermission(permission);
     serverAnswerPromise
       .then((permission: BucketPermissionOut) => {
         if (editPermission.value) {
@@ -251,7 +225,7 @@ function confirmedDeletePermission(bucketName: string, uid: string) {
     formState.loading = true;
     BucketPermissionService.bucketPermissionDeletePermissionForBucket(
       bucketName,
-      uid
+      uid,
     )
       .then(() => {
         permissionDeleted.value = true;
@@ -272,6 +246,7 @@ function updateUser(user: User) {
   permission.uid = user.uid;
   formState.grantee_name = user.display_name;
 }
+
 // Lifecycle Hooks
 // -----------------------------------------------------------------------------
 onMounted(() => {
@@ -279,6 +254,18 @@ onMounted(() => {
   successToast = new Toast("#" + "toast-" + randomIDSuffix);
   updatePermission();
 });
+
+function fromTimestampChanged(target?: HTMLInputElement | null) {
+  permission.from_timestamp = target?.value
+    ? dayjs(target?.value).unix()
+    : undefined;
+}
+
+function toTimestampChanged(target?: HTMLInputElement | null) {
+  permission.to_timestamp = target?.value
+    ? dayjs(target?.value).unix()
+    : undefined;
+}
 </script>
 
 <template>
@@ -334,7 +321,7 @@ onMounted(() => {
     <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:header v-else> Create new Permission</template>
     <template v-slot:extra-button>
       <font-awesome-icon
         v-if="props.deletable"
@@ -368,7 +355,7 @@ onMounted(() => {
               class="form-control-plaintext"
               id="bucketNameInput"
               required
-              :value="bucketNameWithoutTenant"
+              :value="permission.bucket_name"
             />
           </div>
         </div>
@@ -437,7 +424,15 @@ onMounted(() => {
               id="permissionDateFromInput"
               :readonly="formState.readonly"
               :min="dayjs().format('YYYY-MM-DD')"
-              v-model="permission.from_timestamp"
+              :value="
+                permission.from_timestamp
+                  ? dayjs.unix(permission.from_timestamp).format('YYYY-MM-DD')
+                  : undefined
+              "
+              @input="
+                (event) =>
+                  fromTimestampChanged(event.target as HTMLInputElement)
+              "
             />
           </div>
           <label
@@ -453,14 +448,22 @@ onMounted(() => {
               class="form-control"
               id="permissionToFromInput"
               :readonly="formState.readonly"
-              v-model="permission.to_timestamp"
               :min="
                 permission.from_timestamp != null
-                  ? dayjs(permission.from_timestamp)
+                  ? dayjs
+                      .unix(permission.from_timestamp)
                       .add(1, 'day')
                       .format('YYYY-MM-DD')
                   : dayjs().add(1, 'day').format('YYYY-MM-DD')
               "
+              :value="
+                permission.to_timestamp
+                  ? dayjs.unix(permission.to_timestamp).format('YYYY-MM-DD')
+                  : undefined
+              "
+              @input="
+                (event) => toTimestampChanged(event.target as HTMLInputElement)
+              "
             />
           </div>
         </div>
@@ -553,6 +556,7 @@ onMounted(() => {
 .pseudo-link {
   color: var(--bs-secondary);
 }
+
 .pseudo-link:hover {
   color: var(--bs-link-hover-color);
 }
@@ -560,6 +564,7 @@ onMounted(() => {
 .delete-icon {
   color: var(--bs-secondary) !important;
 }
+
 .delete-icon:hover {
   color: var(--bs-danger) !important;
 }
diff --git a/src/components/object-storage/modals/UploadObjectModal.vue b/src/components/object-storage/modals/UploadObjectModal.vue
index b123b883266ce42f14875ebad4f0e42a0a6905a0..79a96f6689a614f38db33a389db3ce2246978559 100644
--- a/src/components/object-storage/modals/UploadObjectModal.vue
+++ b/src/components/object-storage/modals/UploadObjectModal.vue
@@ -5,8 +5,10 @@ import BootstrapModal from "@/components/modals/BootstrapModal.vue";
 import { computed, onMounted, reactive, ref, watch } from "vue";
 import type { S3ObjectMetaInformation } from "@/client/s3proxy";
 import dayjs from "dayjs";
-import { filesize } from "filesize";
 import { Modal, Toast } from "bootstrap";
+import { partial } from "filesize";
+
+const fsize = partial({ base: 2, standard: "jedec" });
 
 const props = defineProps<{
   modalID: string;
@@ -32,7 +34,7 @@ watch(
   () => props.editObjectFileName,
   (nextFileName) => {
     formState.key = nextFileName ?? "";
-  }
+  },
 );
 
 const formState = reactive({
@@ -50,18 +52,13 @@ const formState = reactive({
 });
 
 const uploadProgress = computed<number>(() =>
-  Math.round((100 * formState.uploadDone) / formState.uploadTotal)
+  Math.round((100 * formState.uploadDone) / formState.uploadTotal),
 );
 
 const editObject = computed<boolean>(
-  () => props.editObjectFileName !== undefined
+  () => props.editObjectFileName !== undefined,
 );
 
-const bucketNameWithoutTenant = computed<string>(() => {
-  const splittedBucketName = props.bucketName.split(":");
-  return splittedBucketName[splittedBucketName.length - 1];
-});
-
 async function uploadObject() {
   const key =
     props.keyPrefix.length > 0
@@ -180,7 +177,7 @@ onMounted(() => {
     <template v-slot:header>
       <h4>Upload file to</h4>
       <ol class="breadcrumb">
-        <li class="breadcrumb-item">{{ bucketNameWithoutTenant }}</li>
+        <li class="breadcrumb-item">{{ props.bucketName }}</li>
         <li
           class="breadcrumb-item"
           v-for="folder in currentFolders"
@@ -263,8 +260,8 @@ onMounted(() => {
           </div>
         </div>
         <span v-if="formState.uploadDone > 0">
-          {{ filesize(formState.uploadDone) }} /
-          {{ filesize(formState.uploadTotal) }}
+          {{ fsize(formState.uploadDone) }} /
+          {{ fsize(formState.uploadTotal) }}
         </span>
       </div>
       <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">
diff --git a/src/components/parameter-schema/ParameterSchemaDescriptionComponent.vue b/src/components/parameter-schema/ParameterSchemaDescriptionComponent.vue
index d5b940c916c16a557c3ae41a514600d80b0b9e52..df8d127eafb60b82640405358fbe5a08ffd2429b 100644
--- a/src/components/parameter-schema/ParameterSchemaDescriptionComponent.vue
+++ b/src/components/parameter-schema/ParameterSchemaDescriptionComponent.vue
@@ -30,13 +30,13 @@ const navParameterGroups = computed<ParameterGroup[]>(() =>
       (group) =>
         Object.keys(parameterGroups.value[group.group]["properties"]).filter(
           (key) =>
-            !parameterGroups.value[group.group]["properties"][key]["hidden"]
-        ).length > 0
-    )
+            !parameterGroups.value[group.group]["properties"][key]["hidden"],
+        ).length > 0,
+    ),
 );
 
 const parameterGroups = computed<Record<string, never>>(
-  () => props.schema["definitions"]
+  () => props.schema["definitions"],
 );
 </script>
 
diff --git a/src/components/parameter-schema/ParameterSchemaFormComponent.vue b/src/components/parameter-schema/ParameterSchemaFormComponent.vue
index 096e2ce775528fe9c702bd4fc1bd95416b5c715e..d59dd130a49818b67eef8848ae94754fa04ac6cf 100644
--- a/src/components/parameter-schema/ParameterSchemaFormComponent.vue
+++ b/src/components/parameter-schema/ParameterSchemaFormComponent.vue
@@ -27,7 +27,7 @@ const emit = defineEmits<{
     // eslint-disable-next-line @typescript-eslint/no-explicit-any
     parameters: Record<string, any>,
     notes?: string,
-    report_output_bucket?: string
+    report_output_bucket?: string,
   ): void;
 }>();
 
@@ -73,7 +73,7 @@ const formState = reactive<{
 // Computed Properties
 // =============================================================================
 const parameterGroups = computed<Record<string, never>>(
-  () => props.schema?.["definitions"]
+  () => props.schema?.["definitions"],
 );
 
 // Create a list with the names of all parameter groups
@@ -91,9 +91,9 @@ const navParameterGroups = computed<ParameterGroup[]>(() =>
       (group) =>
         Object.keys(parameterGroups.value[group.group]["properties"]).filter(
           (key) =>
-            !parameterGroups.value[group.group]["properties"][key]["hidden"]
-        ).length > 0
-    )
+            !parameterGroups.value[group.group]["properties"][key]["hidden"],
+        ).length > 0,
+    ),
 );
 
 // Watchers
@@ -104,7 +104,7 @@ watch(
     if (newValue) {
       updateSchema(newValue);
     }
-  }
+  },
 );
 
 // Functions
@@ -122,7 +122,7 @@ function updateSchema(schema: Record<string, any>) {
           parameterName,
           // @ts-ignore
           parameter["default"],
-        ])
+        ]),
     ),
   ]);
   formState.formInput = Object.fromEntries(b);
@@ -134,7 +134,7 @@ function startWorkflow() {
   formState.errorType = undefined;
   if (launchForm.value?.checkValidity()) {
     const realInput: Record<string, any> = Object.values(
-      formState.formInput
+      formState.formInput,
     ).reduce((acc, val) => {
       return { ...acc, ...val };
     });
@@ -148,7 +148,7 @@ function startWorkflow() {
         "start-workflow",
         realInput,
         formState.pipelineNotes,
-        formState.report_bucket
+        formState.report_bucket,
       );
     }
   } else {
diff --git a/src/components/parameter-schema/description-mode/ParameterDescription.vue b/src/components/parameter-schema/description-mode/ParameterDescription.vue
index 4e5f518589d1358343b7a1b54cf8d21cddd1239b..cd7a63ef1c7ac3b2acfb8f780220663e6c3ebc00 100644
--- a/src/components/parameter-schema/description-mode/ParameterDescription.vue
+++ b/src/components/parameter-schema/description-mode/ParameterDescription.vue
@@ -21,27 +21,27 @@ const props = defineProps({
 const randomIDSuffix = Math.random().toString(16).substring(2, 8);
 
 const helpText = computed<string | undefined>(
-  () => props.parameter["help_text"]
+  () => props.parameter["help_text"],
 );
 const parameterType = computed<string>(() => props.parameter["type"]);
 const icon = computed<string | undefined>(() => props.parameter["fa_icon"]);
 const description = computed<string>(() => props.parameter["description"]);
-const defaultValue = computed<string | undefined>(() =>
-  props.parameter["default"]?.toString()
+const defaultValue = computed<string | undefined>(
+  () => props.parameter["default"]?.toString(),
 );
-const enumValues = computed<string[] | undefined>(() =>
-  props.parameter["enum"]?.map((val: string) => val.toString())
+const enumValues = computed<string[] | undefined>(
+  () => props.parameter["enum"]?.map((val: string) => val.toString()),
 );
 const hidden = computed<boolean>(() => props.parameter["hidden"] ?? false);
 const parameterPattern = computed<string | undefined>(
-  () => props.parameter["pattern"]
+  () => props.parameter["pattern"],
 );
 
 const showRightColum = computed<boolean>(
   () =>
     helpText.value != undefined ||
     props.required ||
-    defaultValue.value != undefined
+    defaultValue.value != undefined,
 );
 </script>
 
@@ -74,7 +74,7 @@ const showRightColum = computed<boolean>(
         data-bs-toggle="collapse"
         :data-bs-target="'#helpCollapse' + randomIDSuffix"
         aria-expanded="false"
-        aria-controls="collapseExample"
+        :aria-controls="'helpCollapse' + randomIDSuffix"
         v-if="helpText"
       >
         <font-awesome-icon icon="fa-solid fa-circle-info" />
@@ -109,15 +109,13 @@ const showRightColum = computed<boolean>(
         >required</span
       >
     </div>
-    <div
-      class="collapse p-2 pb-0 border rounded m-2 flex-shrink-1"
-      :id="'helpCollapse' + randomIDSuffix"
-      v-if="helpText"
-    >
-      <markdown-renderer class="helpTextCode" :markdown="helpText" />
-      <span v-if="parameterPattern" class="mb-2"
-        >Pattern: <code>{{ parameterPattern }}</code></span
-      >
+    <div class="collapse" :id="'helpCollapse' + randomIDSuffix" v-if="helpText">
+      <div class="p-2 pb-0 border rounded m-2 flex-shrink-1">
+        <markdown-renderer class="helpTextCode" :markdown="helpText" />
+        <span v-if="parameterPattern" class="mb-2"
+          >Pattern: <code>{{ parameterPattern }}</code></span
+        >
+      </div>
     </div>
   </div>
 </template>
diff --git a/src/components/parameter-schema/description-mode/ParameterGroupDescription.vue b/src/components/parameter-schema/description-mode/ParameterGroupDescription.vue
index f8cc45c630c199a6ac292be346af74cfc8c4bdb9..93b8b9416f0f5379d7a69808a71d98c4129cdf4b 100644
--- a/src/components/parameter-schema/description-mode/ParameterGroupDescription.vue
+++ b/src/components/parameter-schema/description-mode/ParameterGroupDescription.vue
@@ -23,11 +23,11 @@ const description = computed<string>(() => props.parameterGroup["description"]);
 const groupHidden = computed<boolean>(() =>
   Object.keys(parameters.value).reduce(
     (acc: boolean, val: string) => acc && parameters.value[val]["hidden"],
-    true
-  )
+    true,
+  ),
 );
 const parameters = computed<Record<string, never>>(
-  () => props.parameterGroup["properties"]
+  () => props.parameterGroup["properties"],
 );
 </script>
 
diff --git a/src/components/parameter-schema/form-mode/ParameterBooleanInput.vue b/src/components/parameter-schema/form-mode/ParameterBooleanInput.vue
index 54569ffe1b4664d3dbb686047f3264bb70faef88..a3db905214f3d271a513b256fcf681c91516d878 100644
--- a/src/components/parameter-schema/form-mode/ParameterBooleanInput.vue
+++ b/src/components/parameter-schema/form-mode/ParameterBooleanInput.vue
@@ -26,7 +26,7 @@ const randomIDSuffix = Math.random().toString(16).substring(2, 8);
 
 const helpTextPresent = computed<boolean>(() => props.parameter["help_text"]);
 const defaultValue = computed<boolean>(
-  () => props.parameter["default"] ?? false
+  () => props.parameter["default"] ?? false,
 );
 
 const emit = defineEmits<{
diff --git a/src/components/parameter-schema/form-mode/ParameterGroupForm.vue b/src/components/parameter-schema/form-mode/ParameterGroupForm.vue
index 34bab4686b68ab76d261b9a2807446c3a343af8b..f84a78da9d0b2b993fa901f47ac6a6eede04327f 100644
--- a/src/components/parameter-schema/form-mode/ParameterGroupForm.vue
+++ b/src/components/parameter-schema/form-mode/ParameterGroupForm.vue
@@ -30,25 +30,25 @@ const description = computed<string>(() => props.parameterGroup["description"]);
 const groupHidden = computed<boolean>(() =>
   Object.keys(parameters.value).reduce(
     (acc: boolean, val: string) => acc && parameters.value[val]["hidden"],
-    true
-  )
+    true,
+  ),
 );
 const parameters = computed<Record<string, never>>(
-  () => props.parameterGroup["properties"]
+  () => props.parameterGroup["properties"],
 );
 
 const formInput = computed(() => props.modelValue);
 const emit = defineEmits<{
   (
     e: "update:modelValue",
-    value: Record<string, number | string | boolean | undefined>
+    value: Record<string, number | string | boolean | undefined>,
   ): void;
 }>();
 
 function parameterRequired(
   // eslint-disable-next-line @typescript-eslint/no-explicit-any
   parameterGroup: Record<string, any>,
-  parameterName: string
+  parameterName: string,
 ): boolean {
   return (
     parameterGroup["required"]?.includes(parameterName) || // parameter is required
@@ -66,7 +66,7 @@ watch(
   },
   {
     deep: true,
-  }
+  },
 );
 </script>
 
@@ -144,7 +144,7 @@ watch(
               data-bs-toggle="collapse"
               :data-bs-target="'#helpCollapse' + parameterName"
               aria-expanded="false"
-              aria-controls="collapseExample"
+              :aria-controls="'helpCollapse' + parameterName"
             >
               <font-awesome-icon
                 class="cursor-pointer"
@@ -152,21 +152,23 @@ watch(
               />
             </span>
           </div>
-          <label v-if="parameter['description']"
-            ><markdown-renderer :markdown="parameter['description']"
-          /></label>
+          <label v-if="parameter['description']">
+            <markdown-renderer :markdown="parameter['description']" />
+          </label>
           <div
-            class="collapse p-2 pb-0 mx-2 mb-2 mt-1 flex-shrink-1 border rounded"
+            class="collapse"
             :id="'helpCollapse' + parameterName"
             v-if="parameter['help_text']"
           >
-            <markdown-renderer
-              class="helpTextCode"
-              :markdown="parameter['help_text']"
-            />
-            <p v-if="parameter['pattern']">
-              Pattern: <code>{{ parameter["pattern"] }}</code>
-            </p>
+            <div class="p-2 pb-0 mx-2 mb-2 mt-1 flex-shrink-1 border rounded">
+              <markdown-renderer
+                class="helpTextCode"
+                :markdown="parameter['help_text']"
+              />
+              <p v-if="parameter['pattern']">
+                Pattern: <code>{{ parameter["pattern"] }}</code>
+              </p>
+            </div>
           </div>
         </template>
       </template>
@@ -178,6 +180,7 @@ watch(
 div.card-body {
   backdrop-filter: brightness(1.2);
 }
+
 span.cursor-pointer:hover {
   color: var(--bs-info);
   background: var(--bs-light);
diff --git a/src/components/parameter-schema/form-mode/ParameterStringInput.vue b/src/components/parameter-schema/form-mode/ParameterStringInput.vue
index 7019b22ba07eb86d7c5fb57cb0e0dbb25d5b1182..1ca1b8b9e07035506376f1b74164e462c967acd5 100644
--- a/src/components/parameter-schema/form-mode/ParameterStringInput.vue
+++ b/src/components/parameter-schema/form-mode/ParameterStringInput.vue
@@ -58,7 +58,7 @@ watch(
     if (newVal !== oldVal) {
       updateKeysInBucket(newVal);
     }
-  }
+  },
 );
 
 const pattern = computed<string>(() => props.parameter["pattern"]);
@@ -72,7 +72,7 @@ const stringInput = ref<HTMLInputElement | undefined>(undefined);
 const format = computed<string | undefined>(() => props.parameter["format"]);
 
 const filesInBucket = computed<string[]>(() =>
-  keysInBucket.value.filter((obj) => !obj.endsWith("/"))
+  keysInBucket.value.filter((obj) => !obj.endsWith("/")),
 );
 
 const foldersInBucket = computed<string[]>(() =>
@@ -82,15 +82,15 @@ const foldersInBucket = computed<string[]>(() =>
       return parts
         .slice(0, parts.length - 1)
         .map((part, index) =>
-          parts.slice(0, index + 1).reduce((acc, val) => `${acc}/${val}`)
+          parts.slice(0, index + 1).reduce((acc, val) => `${acc}/${val}`),
         );
     })
     .flat()
-    .filter((val, index, array) => array.indexOf(val) === index)
+    .filter((val, index, array) => array.indexOf(val) === index),
 );
 
 const filesAndFoldersInBucket = computed<string[]>(() =>
-  filesInBucket.value.concat(foldersInBucket.value)
+  filesInBucket.value.concat(foldersInBucket.value),
 );
 
 const keyDataList = computed<string[]>(() => {
@@ -112,12 +112,12 @@ function updateValue() {
       "update:modelValue",
       !s3Path.bucket && s3Path.key
         ? undefined
-        : `s3://${s3Path.bucket}/${s3Path.key ?? ""}`
+        : `s3://${s3Path.bucket}/${s3Path.key ?? ""}`,
     );
   } else {
     emit(
       "update:modelValue",
-      stringInput.value?.value ? stringInput.value?.value : undefined
+      stringInput.value?.value ? stringInput.value?.value : undefined,
     );
   }
 }
diff --git a/src/components/workflows/WorkflowCard.vue b/src/components/workflows/WorkflowCard.vue
index c86514edcb40f14a2bc75094847a7abbe636271b..61f2beb88232deac2326c7a36efedb60c38f3074 100644
--- a/src/components/workflows/WorkflowCard.vue
+++ b/src/components/workflows/WorkflowCard.vue
@@ -1,5 +1,5 @@
 <script setup lang="ts">
-import type { WorkflowOut, WorkflowVersionReduced } from "@/client/workflow";
+import type { WorkflowOut, WorkflowVersion } from "@/client/workflow";
 import FontAwesomeIcon from "@/components/FontAwesomeIcon.vue";
 import dayjs from "dayjs";
 import { onMounted, ref, computed } from "vue";
@@ -13,8 +13,8 @@ const props = defineProps<{
 
 const randomIDSuffix: string = Math.random().toString(16).substring(2, 8);
 const truncateDescription = ref<boolean>(true);
-const latestVersion = computed<WorkflowVersionReduced | undefined>(() =>
-  calculateLatestVersion(props.workflow.versions)
+const latestVersion = computed<WorkflowVersion | undefined>(() =>
+  calculateLatestVersion(props.workflow.versions),
 );
 
 onMounted(() => {
@@ -69,31 +69,34 @@ onMounted(() => {
         <div v-if="props.loading" class="placeholder-glow w-50">
           <span class="placeholder placeholder-lg w-50 bg-success"></span>
         </div>
-        <a
+        <router-link
           v-else
-          :href="
-            workflow.repository_url + '/tree/' + latestVersion?.git_commit_hash
-          "
-          target="_blank"
+          :to="{
+            name: 'workflow-version',
+            params: {
+              workflowId: workflow.workflow_id,
+              versionId: latestVersion?.git_commit_hash,
+            },
+          }"
           class="btn btn-outline-success"
           role="button"
         >
           <font-awesome-icon class="fs-5" icon="fa-solid fa-tag" />
           {{ latestVersion?.version }}
-        </a>
+        </router-link>
         <div v-if="props.loading" class="placeholder-glow w-25">
           <span class="placeholder w-100 bg-secondary"></span>
         </div>
         <span
-          v-else
+          v-else-if="latestVersion"
           :id="'creationDate-' + randomIDSuffix"
           data-bs-toggle="tooltip"
           class="align-self-end text-secondary"
           :data-bs-title="
-            dayjs(workflow.versions[0].created_at).format('DD.MM.YYYY HH:mm:ss')
+            dayjs.unix(latestVersion?.created_at).format('DD.MM.YYYY HH:mm:ss')
           "
         >
-          {{ dayjs(latestVersion?.created_at).fromNow() }}
+          {{ dayjs.unix(latestVersion?.created_at).fromNow() }}
         </span>
       </div>
     </div>
@@ -112,7 +115,7 @@ onMounted(() => {
 }
 
 .icon {
-  max-height: 30px;
-  max-width: 30px;
+  max-height: 32px;
+  max-width: 32px;
 }
 </style>
diff --git a/src/components/workflows/WorkflowDocumentationTabs.vue b/src/components/workflows/WorkflowDocumentationTabs.vue
index 6fd048de6e9edf84526d700274baef99a92e6d34..44a1c9a3f593689729d3e9c60cfd7d9329f3d16d 100644
--- a/src/components/workflows/WorkflowDocumentationTabs.vue
+++ b/src/components/workflows/WorkflowDocumentationTabs.vue
@@ -16,7 +16,7 @@ const props = defineProps<{
 }>();
 
 const activeTab = computed<string>(
-  () => (route.query["tab"] as string) ?? "description"
+  () => (route.query["tab"] as string) ?? "description",
 );
 </script>
 
diff --git a/src/components/workflows/WorkflowStatisticsChart.vue b/src/components/workflows/WorkflowStatisticsChart.vue
index c9d5e5f5a30098b0c048b1569e8b361b85247406..ee4711cfc8829daa8390973a303b1af6aaf1da07 100644
--- a/src/components/workflows/WorkflowStatisticsChart.vue
+++ b/src/components/workflows/WorkflowStatisticsChart.vue
@@ -35,7 +35,7 @@ Chart.register(
   TimeSeriesScale,
   LineController,
   LineElement,
-  PointElement
+  PointElement,
 );
 
 // eslint-disable-next-line @typescript-eslint/ban-ts-comment
@@ -57,7 +57,7 @@ function updateData(chart: Chart, newStats: WorkflowStatistic[]) {
     (
       (sum) => (value) =>
         (sum += value.count)
-    )(0)
+    )(0),
   );
   // eslint-disable-next-line @typescript-eslint/ban-ts-comment
   // @ts-ignore
@@ -97,7 +97,7 @@ const paddedStats = computed<WorkflowStatistic[]>(() => {
       firstDay = lastYear;
       // Find index in stats list where the stat is from a day after the same day last year
       statsIndex = props.stats.findIndex((stat) =>
-        lastYear.isSameOrBefore(dayjs(stat.day), "day")
+        lastYear.isSameOrBefore(dayjs(stat.day), "day"),
       );
     }
   } else {
@@ -229,7 +229,7 @@ onMounted(() => {
                   // @ts-ignore
                   tempChart.chart.options.scales.x.time.unit = xAxisLabelUnit(
                     tempChart.chart.scales.x.min,
-                    tempChart.chart.scales.x.max
+                    tempChart.chart.scales.x.max,
                   );
                   tempChart.chart.update();
                   disableZoomReset.value = !tempChart.chart.isZoomedOrPanned();
diff --git a/src/components/workflows/WorkflowWithVersionsCard.vue b/src/components/workflows/WorkflowWithVersionsCard.vue
index 6c39b9a2e6758e7dd0c37ed31269df6ef6ef0d50..0e72ccb8b29d9f807d5b2fc914dc1ca03d005460 100644
--- a/src/components/workflows/WorkflowWithVersionsCard.vue
+++ b/src/components/workflows/WorkflowWithVersionsCard.vue
@@ -1,28 +1,47 @@
 <script setup lang="ts">
-import type { WorkflowOut } from "@/client/workflow";
-import { ref } from "vue";
+import type { WorkflowOut, WorkflowVersion } from "@/client/workflow";
+import { onMounted, ref, watch } from "vue";
 import { Status } from "@/client/workflow";
 import FontAwesomeIcon from "@/components/FontAwesomeIcon.vue";
 import dayjs from "dayjs";
 import { sortedVersions } from "@/utils/Workflow";
+import { Tooltip } from "bootstrap";
 
 const props = defineProps<{
   workflow: WorkflowOut;
   loading: boolean;
 }>();
 const truncateDescription = ref<boolean>(true);
+const randomIDSuffix: string = Math.random().toString(16).substring(2, 8);
 
 const emit = defineEmits<{
   (e: "workflow-update-click", workflow: WorkflowOut): void;
   (e: "workflow-delete-click", workflow: WorkflowOut): void;
+  (e: "workflow-update-credentials-click", workflow: WorkflowOut): void;
+  (e: "workflow-update-icon-click", version: WorkflowVersion): void;
 }>();
 
+watch(
+  () => props.loading,
+  (loading) => {
+    if (!loading && props.workflow.private) {
+      new Tooltip(`#tooltip-${randomIDSuffix}`);
+    }
+  },
+);
+
 const statusToIconMapping: Record<string, string> = {
   PUBLISHED: "fa-solid fa-circle-check",
   DENIED: "fa-solid fa-x",
   CREATED: "fa-solid fa-circle-pause",
   DEPRECATED: "fa-solid fa-box-archive",
 };
+
+onMounted(() => {
+  if (!props.loading && props.workflow.private) {
+    new Tooltip(`#tooltip-${randomIDSuffix}`);
+  }
+});
 </script>
 
 <template>
@@ -34,8 +53,28 @@ const statusToIconMapping: Record<string, string> = {
         <div v-if="props.loading" class="placeholder-glow w-100">
           <span class="placeholder col-6"></span>
         </div>
-        <span v-else class="text-truncate">{{ props.workflow.name }}</span>
+        <div v-else class="text-truncate">
+          <font-awesome-icon
+            v-if="props.workflow.private"
+            icon="fa-solid fa-lock"
+            class="fs-5 me-2 tooltip-private-repository"
+            :id="'tooltip-' + randomIDSuffix"
+            data-bs-toggle="tooltip"
+            data-bs-title="Private Git Repository"
+          />
+          <span>{{ props.workflow.name }}</span>
+        </div>
         <div>
+          <button
+            type="button"
+            class="btn btn-outline-info me-2"
+            @click="emit('workflow-update-credentials-click', props.workflow)"
+            :class="{ disabled: props.loading }"
+            data-bs-toggle="modal"
+            data-bs-target="#updateWorkflowCredentialsModal"
+          >
+            <font-awesome-icon icon="fa-solid fa-key" />
+          </button>
           <button
             type="button"
             class="btn btn-outline-danger me-2"
@@ -85,13 +124,6 @@ const statusToIconMapping: Record<string, string> = {
                 v-for="version in sortedVersions(props.workflow.versions)"
                 :key="version.git_commit_hash"
               >
-                <td class="w-fit">
-                  <img
-                    v-if="version.icon_url != null"
-                    :src="version.icon_url"
-                    alt="Workflow Version Icon"
-                  />
-                </td>
                 <th scope="row" class="fw-bold">{{ version.version }}</th>
                 <td
                   :class="{
@@ -106,7 +138,28 @@ const statusToIconMapping: Record<string, string> = {
                   />
                   {{ version.status }}
                 </td>
-                <td>{{ dayjs(version.created_at).format("D MMMM YYYY") }}</td>
+                <td>
+                  {{ dayjs.unix(version.created_at).format("D MMMM YYYY") }}
+                </td>
+                <td class="w-fit">
+                  <img
+                    v-if="version.icon_url != null"
+                    :src="version.icon_url"
+                    alt="Workflow Version Icon"
+                    @click="emit('workflow-update-icon-click', version)"
+                    class="cursor-pointer"
+                    data-bs-toggle="modal"
+                    data-bs-target="#updateWorkflowVersionIconModal"
+                  />
+                  <font-awesome-icon
+                    v-else
+                    icon="fa-solid fa-circle-plus"
+                    class="add-icon-hover cursor-pointer"
+                    @click="emit('workflow-update-icon-click', version)"
+                    data-bs-toggle="modal"
+                    data-bs-target="#updateWorkflowVersionIconModal"
+                  />
+                </td>
                 <td>
                   <router-link
                     class="w-fit mx-0"
@@ -117,8 +170,8 @@ const statusToIconMapping: Record<string, string> = {
                         versionId: version.git_commit_hash,
                       },
                     }"
-                    >View</router-link
-                  >
+                    >View
+                  </router-link>
                 </td>
               </tr>
             </tbody>
@@ -145,8 +198,13 @@ const statusToIconMapping: Record<string, string> = {
   transform: translate(0, -5px);
   box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15) !important;
 }
+
 td > img {
   max-width: 1em;
   max-height: 1em;
 }
+
+.add-icon-hover:hover {
+  color: var(--bs-success) !important;
+}
 </style>
diff --git a/src/components/workflows/modals/ArbitraryWorkflowModal.vue b/src/components/workflows/modals/ArbitraryWorkflowModal.vue
index ef8967082f11f868d39d73ec83c34553d92f9a11..9abbaf2783fa0d6868361493de778f1114a70f86 100644
--- a/src/components/workflows/modals/ArbitraryWorkflowModal.vue
+++ b/src/components/workflows/modals/ArbitraryWorkflowModal.vue
@@ -1,20 +1,22 @@
 <script setup lang="ts">
 import BootstrapModal from "@/components/modals/BootstrapModal.vue";
 import FontAwesomeIcon from "@/components/FontAwesomeIcon.vue";
-import { computed, onMounted, reactive, ref } from "vue";
+import { computed, onMounted, reactive, ref, watch } from "vue";
 import { useRouter } from "vue-router";
 import {
   GitRepository,
   requiredRepositoryFiles,
   determineGitIcon,
 } from "@/utils/GitRepository";
-import { Modal } from "bootstrap";
+import { Collapse, Modal } from "bootstrap";
 
 const props = defineProps<{
   modalID: string;
 }>();
 
 let createWorkflowModal: Modal | null = null;
+let privateRepositoryCollapse: Collapse | null = null;
+let tokenHelpCollapse: Collapse | null = null;
 const arbitraryWorkflowForm = ref<HTMLFormElement | undefined>(undefined);
 const workflowRepositoryElement = ref<HTMLInputElement | undefined>(undefined);
 const router = useRouter();
@@ -27,6 +29,14 @@ const workflow = reactive<{
   git_commit_hash: "",
 });
 
+const repositoryCredentials = reactive<{
+  token: string;
+  privateRepo: boolean;
+}>({
+  token: "",
+  privateRepo: false,
+});
+
 const formState = reactive<{
   loading: boolean;
   checkRepoLoading: boolean;
@@ -43,18 +53,34 @@ const formState = reactive<{
   unsupportedRepository: false,
 });
 
+watch(
+  () => repositoryCredentials.privateRepo,
+  (show) => {
+    if (show) {
+      privateRepositoryCollapse?.show();
+    } else {
+      privateRepositoryCollapse?.hide();
+      tokenHelpCollapse?.hide();
+    }
+  },
+);
+
 function modalClosed() {
   formState.validated = false;
+  tokenHelpCollapse?.hide();
 }
 
 function viewWorkflow() {
-  console.log("View", workflow);
   createWorkflowModal?.hide();
   router.push({
     name: "arbitrary-workflow",
     query: {
       repository: encodeURI(workflow.repository_url),
       commit_hash: workflow.git_commit_hash,
+      token:
+        repositoryCredentials.token.length > 0
+          ? encodeURIComponent(repositoryCredentials.token)
+          : undefined,
     },
   });
 }
@@ -68,7 +94,10 @@ function checkRepository() {
     try {
       const repo = GitRepository.buildRepository(
         workflow.repository_url,
-        workflow.git_commit_hash
+        workflow.git_commit_hash,
+        repositoryCredentials.privateRepo
+          ? repositoryCredentials.token
+          : undefined,
       );
       repo
         .checkFilesExist(requiredRepositoryFiles, true)
@@ -80,7 +109,7 @@ function checkRepository() {
           // Allow execution of the workflow if main.nf and parameter schema are not missing
           if (
             formState.missingFiles.findIndex(
-              (file) => file === "main.nf" || file === "nextflow_schema.json"
+              (file) => file === "main.nf" || file === "nextflow_schema.json",
             ) < 0
           ) {
             formState.allowUpload = true;
@@ -89,17 +118,24 @@ function checkRepository() {
     } catch (e) {
       formState.unsupportedRepository = true;
       workflowRepositoryElement.value?.setCustomValidity(
-        "Repository is not supported"
+        "Repository is not supported",
       );
     }
   }
 }
+
 const gitIcon = computed<string>(() =>
-  determineGitIcon(workflow.repository_url)
+  determineGitIcon(workflow.repository_url),
 );
 
 onMounted(() => {
   createWorkflowModal = new Modal("#" + props.modalID);
+  privateRepositoryCollapse = new Collapse("#privateRepositoryCollapse", {
+    toggle: false,
+  });
+  tokenHelpCollapse = new Collapse("#tokenHelpCollapse", {
+    toggle: false,
+  });
 });
 </script>
 
@@ -177,6 +213,73 @@ onMounted(() => {
             </li>
           </ul>
         </div>
+        <div class="mb-3">
+          <div class="form-check fs-5">
+            <input
+              class="form-check-input"
+              type="checkbox"
+              v-model="repositoryCredentials.privateRepo"
+              id="privateRepositoryCheckbox"
+              @change="formState.allowUpload = false"
+              aria-controls="#privateRepositoryCollapse"
+            />
+            <label class="form-check-label" for="privateRepositoryCheckbox">
+              Private Git Repository
+            </label>
+          </div>
+          <div class="collapse" id="privateRepositoryCollapse">
+            <label for="tokenUsernameInput" class="form-label">Token</label>
+            <div class="input-group">
+              <div class="input-group-text">
+                <font-awesome-icon icon="fa-solid fa-key" />
+              </div>
+              <input
+                type="password"
+                class="form-control"
+                id="repositoryTokenInput"
+                v-model="repositoryCredentials.token"
+                @change="formState.allowUpload = false"
+                :required="repositoryCredentials.privateRepo"
+                aria-controls="#tokenHelpCollapse"
+              />
+              <div
+                class="input-group-text cursor-pointer hover-info"
+                @click="tokenHelpCollapse?.toggle()"
+              >
+                <font-awesome-icon icon="fa-solid fa-circle-question" />
+              </div>
+            </div>
+            <div class="collapse" id="tokenHelpCollapse">
+              <div class="card card-body mt-3">
+                <h5>GitHub</h5>
+                <p>
+                  For private GitHub repositories, CloWM needs a Personal Access
+                  Token (classic) with the scope <code>repo</code>.<br />
+                  Read this
+                  <a
+                    target="_blank"
+                    href="https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens#creating-a-personal-access-token-classic"
+                    >Tutorial</a
+                  >
+                  on how to create such a token.
+                </p>
+                <h5>GitLab</h5>
+                <p>
+                  For private GitLab repositories, CloWM needs a Project Access
+                  Token with the <code>read_api</code> scope and at least
+                  <code>Reporter</code> role.<br />
+                  Read this
+                  <a
+                    target="_blank"
+                    href="https://docs.gitlab.com/ee/user/project/settings/project_access_tokens.html#create-a-project-access-token"
+                    >Tutorial</a
+                  >
+                  on how to create such a token.
+                </p>
+              </div>
+            </div>
+          </div>
+        </div>
       </form>
     </template>
     <template v-slot:footer>
@@ -216,4 +319,8 @@ onMounted(() => {
   </bootstrap-modal>
 </template>
 
-<style scoped></style>
+<style scoped>
+.hover-info:hover {
+  color: var(--bs-info) !important;
+}
+</style>
diff --git a/src/components/workflows/modals/CreateWorkflowModal.vue b/src/components/workflows/modals/CreateWorkflowModal.vue
index 238da5033b08939566f1086995cea99706e57977..3971a16223b875c5fb295d56bcaf601f7b02c631 100644
--- a/src/components/workflows/modals/CreateWorkflowModal.vue
+++ b/src/components/workflows/modals/CreateWorkflowModal.vue
@@ -1,10 +1,7 @@
 <script setup lang="ts">
-import { computed, onMounted, reactive, ref } from "vue";
-import { Modal, Toast } from "bootstrap";
-import type {
-  Body_Workflow_create_workflow,
-  WorkflowOut,
-} from "@/client/workflow";
+import { computed, onMounted, reactive, ref, watch } from "vue";
+import { Modal, Toast, Collapse, Tooltip } from "bootstrap";
+import type { WorkflowIn, WorkflowOut } from "@/client/workflow";
 import BootstrapModal from "@/components/modals/BootstrapModal.vue";
 import FontAwesomeIcon from "@/components/FontAwesomeIcon.vue";
 import { ApiError, WorkflowService } from "@/client/workflow";
@@ -31,14 +28,15 @@ const props = defineProps<{
 // =============================================================================
 let createWorkflowModal: Modal | null = null;
 let successToast: Toast | null = null;
+let privateRepositoryCollapse: Collapse | null = null;
+let tokenHelpCollapse: Collapse | null = null;
 
 // HTML Form Elements
 // =============================================================================
 const workflowCreateForm = ref<HTMLFormElement | undefined>(undefined);
-const workflowIconInput = ref<HTMLInputElement | undefined>(undefined);
 const workflowVersionElement = ref<HTMLInputElement | undefined>(undefined);
 const workflowGitCommitHashElement = ref<HTMLInputElement | undefined>(
-  undefined
+  undefined,
 );
 const workflowNameElement = ref<HTMLInputElement | undefined>(undefined);
 const workflowRepositoryElement = ref<HTMLInputElement | undefined>(undefined);
@@ -49,13 +47,14 @@ const randomIDSuffix = Math.random().toString(16).substring(2, 8);
 
 // Reactive State
 // =============================================================================
-const workflow = reactive<Body_Workflow_create_workflow>({
-  icon: undefined,
+const workflow = reactive<WorkflowIn>({
   name: "",
   short_description: "",
   repository_url: "",
   git_commit_hash: "",
   initial_version: undefined,
+  token: undefined,
+  modes: [],
 });
 
 const formState = reactive<{
@@ -74,10 +73,30 @@ const formState = reactive<{
   unsupportedRepository: false,
 });
 
+const repositoryCredentials = reactive<{
+  token: string;
+  privateRepo: boolean;
+}>({
+  token: "",
+  privateRepo: false,
+});
+
 // Computed Properties
 // =============================================================================
 const gitIcon = computed<string>(() =>
-  determineGitIcon(workflow.repository_url)
+  determineGitIcon(workflow.repository_url),
+);
+
+watch(
+  () => repositoryCredentials.privateRepo,
+  (show) => {
+    if (show) {
+      privateRepositoryCollapse?.show();
+    } else {
+      privateRepositoryCollapse?.hide();
+      tokenHelpCollapse?.hide();
+    }
+  },
 );
 
 // Functions
@@ -90,6 +109,7 @@ function modalClosed() {
   workflowGitCommitHashElement.value?.setCustomValidity("");
   workflowRepositoryElement.value?.setCustomValidity("");
   workflowNameElement.value?.setCustomValidity("");
+  tokenHelpCollapse?.hide();
 }
 
 /**
@@ -104,6 +124,12 @@ function createWorkflow() {
     formState.loading = true;
     workflowNameElement.value?.setCustomValidity("");
     workflowGitCommitHashElement.value?.setCustomValidity("");
+    if (
+      repositoryCredentials.privateRepo &&
+      repositoryCredentials.token.length > 0
+    ) {
+      workflow.token = repositoryCredentials.token;
+    }
     WorkflowService.workflowCreateWorkflow(workflow)
       .then((w) => {
         emit("workflow-created", w);
@@ -117,7 +143,7 @@ function createWorkflow() {
           workflowNameElement.value?.setCustomValidity("Name is already taken");
         } else if (errorText.startsWith("Workflow with git_commit_hash")) {
           workflowGitCommitHashElement.value?.setCustomValidity(
-            "Git commit is already used by a workflow"
+            "Git commit is already used by a workflow",
           );
         }
       })
@@ -132,38 +158,41 @@ function createWorkflow() {
  */
 function resetForm() {
   modalClosed();
-  workflow.icon = undefined;
   workflow.name = "";
   workflow.short_description = "";
   workflow.repository_url = "";
   workflow.git_commit_hash = "";
   workflow.initial_version = undefined;
-  if (workflowIconInput.value != undefined) {
-    workflowIconInput.value.value = "";
-  }
+  workflow.token = undefined;
+  repositoryCredentials.privateRepo = false;
+  repositoryCredentials.token = "";
+  privateRepositoryCollapse?.hide();
 }
 
 /**
  * Watcher function for the file upload in the form.
  */
-function iconChanged() {
-  workflow.icon = workflowIconInput.value?.files?.[0].slice();
-}
+//function iconChanged() {
+//  workflow.icon = workflowIconInput.value?.files?.[0].slice();
+//}
 
 /**
  * Check the workflow repository for the necessary files.
  */
 function checkRepository() {
   formState.validated = true;
+  workflowRepositoryElement.value?.setCustomValidity("");
+  workflowGitCommitHashElement.value?.setCustomValidity("");
   if (workflowCreateForm.value?.checkValidity() && !formState.allowUpload) {
     formState.unsupportedRepository = false;
     formState.missingFiles = [];
-    workflowRepositoryElement.value?.setCustomValidity("");
-    workflowGitCommitHashElement.value?.setCustomValidity("");
     try {
       const repo = GitRepository.buildRepository(
         workflow.repository_url,
-        workflow.git_commit_hash
+        workflow.git_commit_hash,
+        repositoryCredentials.privateRepo
+          ? repositoryCredentials.token
+          : undefined,
       );
       repo
         .checkFilesExist(requiredRepositoryFiles, true)
@@ -173,13 +202,13 @@ function checkRepository() {
         .catch((e: Error) => {
           formState.missingFiles = e.message.split(",");
           workflowGitCommitHashElement.value?.setCustomValidity(
-            "Files are missing in the repository"
+            "Files are missing in the repository",
           );
         });
     } catch (e) {
       formState.unsupportedRepository = true;
       workflowRepositoryElement.value?.setCustomValidity(
-        "Repository is not supported"
+        "Repository is not supported",
       );
     }
   }
@@ -191,7 +220,7 @@ function checkRepository() {
 function checkVersionValidity() {
   if (valid(workflow.initial_version) == null) {
     workflowVersionElement.value?.setCustomValidity(
-      "Please use semantic versioning"
+      "Please use semantic versioning",
     );
   } else {
     workflowVersionElement.value?.setCustomValidity("");
@@ -203,6 +232,15 @@ function checkVersionValidity() {
 onMounted(() => {
   createWorkflowModal = new Modal("#" + props.modalID);
   successToast = new Toast("#successToast-" + randomIDSuffix);
+  privateRepositoryCollapse = new Collapse("#privateRepositoryCollapse", {
+    toggle: false,
+  });
+  tokenHelpCollapse = new Collapse("#tokenHelpCollapse", {
+    toggle: false,
+  });
+  new Tooltip("#tooltip-version-" + randomIDSuffix);
+  new Tooltip("#tooltip-commit-" + randomIDSuffix);
+  new Tooltip("#tooltip-url-" + randomIDSuffix);
 });
 </script>
 
@@ -272,10 +310,7 @@ onMounted(() => {
               placeholder="Describe the purpose of the workflow in a few words."
             ></textarea>
             <div class="invalid-feedback">
-              Requirements
-              <ul class="mb-0">
-                <li>At least 64 Characters long</li>
-              </ul>
+              Description needs to be at least 64 characters long.
             </div>
           </div>
         </div>
@@ -298,9 +333,17 @@ onMounted(() => {
               @change="formState.allowUpload = false"
               aria-describedby="gitRepoProviderHelp"
             />
+            <div
+              class="input-group-text hover-info"
+              :id="'tooltip-url-' + randomIDSuffix"
+              data-bs-toggle="tooltip"
+              data-bs-title="The URL of the git repository containing the workflow"
+            >
+              <font-awesome-icon icon="fa-solid fa-circle-question" />
+            </div>
           </div>
           <div id="gitRepoProviderHelp" class="form-text">
-            We support Github and GitLab Repositories
+            We support GitHub and GitLab Repositories
           </div>
           <div class="text-danger">
             <div v-if="formState.unsupportedRepository">
@@ -308,37 +351,45 @@ onMounted(() => {
             </div>
           </div>
         </div>
-        <div class="mb-3">
-          <label for="workflowGitCommitInput" class="form-label"
-            >Git Commit Hash</label
-          >
-          <div class="input-group">
-            <div class="input-group-text">
-              <font-awesome-icon icon="fa-solid fa-code-commit" />
+        <div class="row mb-3">
+          <div class="col-8">
+            <label for="workflowGitCommitInput" class="form-label"
+              >Git Commit Hash</label
+            >
+            <div class="input-group">
+              <div class="input-group-text">
+                <font-awesome-icon icon="fa-solid fa-code-commit" />
+              </div>
+              <input
+                type="text"
+                class="form-control text-lowercase"
+                id="workflowGitCommitInput"
+                placeholder="ba8bcd9..."
+                required
+                ref="workflowGitCommitHashElement"
+                maxlength="40"
+                pattern="[0-9a-f]{40}"
+                v-model="workflow.git_commit_hash"
+                @change="formState.allowUpload = false"
+              />
+              <div
+                class="input-group-text hover-info"
+                :id="'tooltip-commit-' + randomIDSuffix"
+                data-bs-toggle="tooltip"
+                data-bs-title="Hash of the Git commit used for the initial version"
+              >
+                <font-awesome-icon icon="fa-solid fa-circle-question" />
+              </div>
+            </div>
+            <div v-if="formState.missingFiles.length > 0" class="text-danger">
+              The following files are missing in the repository
+              <ul>
+                <li v-for="file in formState.missingFiles" :key="file">
+                  {{ file }}
+                </li>
+              </ul>
             </div>
-            <input
-              type="text"
-              class="form-control text-lowercase"
-              id="workflowGitCommitInput"
-              placeholder="ba8bcd9..."
-              required
-              ref="workflowGitCommitHashElement"
-              maxlength="40"
-              pattern="[0-9a-f]{40}"
-              v-model="workflow.git_commit_hash"
-              @change="formState.allowUpload = false"
-            />
           </div>
-        </div>
-        <div v-if="formState.missingFiles.length > 0" class="text-danger">
-          The following files are missing in the repository
-          <ul>
-            <li v-for="file in formState.missingFiles" :key="file">
-              {{ file }}
-            </li>
-          </ul>
-        </div>
-        <div class="row mb-3">
           <div class="col-4">
             <label for="workflowVersionInput" class="form-label"
               >Initial Version</label
@@ -357,20 +408,86 @@ onMounted(() => {
                 @change="checkVersionValidity"
                 v-model="workflow.initial_version"
               />
+              <div
+                class="input-group-text hover-info"
+                :id="'tooltip-version-' + randomIDSuffix"
+                data-bs-toggle="tooltip"
+                data-bs-title="Should follow semantic versioning"
+              >
+                <font-awesome-icon icon="fa-solid fa-circle-question" />
+              </div>
             </div>
           </div>
-          <div class="col-8">
-            <label for="workflowIconInput" class="form-label"
-              >Optional Icon</label
-            >
+        </div>
+        <div class="mb-3">
+          <div class="form-check fs-5">
             <input
-              type="file"
-              ref="workflowIconInput"
-              accept="image/*"
-              class="form-control"
-              id="workflowIconInput"
-              @change="iconChanged"
+              class="form-check-input"
+              type="checkbox"
+              v-model="repositoryCredentials.privateRepo"
+              id="privateRepositoryCheckbox"
+              @change="formState.allowUpload = false"
+              aria-controls="#privateRepositoryCollapse"
             />
+            <label class="form-check-label" for="privateRepositoryCheckbox">
+              Private Git Repository
+            </label>
+          </div>
+          <div class="collapse" id="privateRepositoryCollapse">
+            <label for="tokenUsernameInput" class="form-label">Token</label>
+            <div class="input-group">
+              <div class="input-group-text">
+                <font-awesome-icon icon="fa-solid fa-key" />
+              </div>
+              <input
+                type="password"
+                class="form-control"
+                id="repositoryTokenInput"
+                v-model="repositoryCredentials.token"
+                @change="formState.allowUpload = false"
+                :required="repositoryCredentials.privateRepo"
+                aria-controls="#tokenHelpCollapse"
+              />
+              <div
+                class="input-group-text cursor-pointer hover-info"
+                @click="tokenHelpCollapse?.toggle()"
+              >
+                <font-awesome-icon icon="fa-solid fa-circle-question" />
+              </div>
+            </div>
+            <div class="collapse" id="tokenHelpCollapse">
+              <div class="card card-body mt-3">
+                <h5>GitHub</h5>
+                <p>
+                  For private GitHub repositories, CloWM needs a Personal Access
+                  Token (classic) with the scope <code>repo</code>.<br />
+                  Read this
+                  <a
+                    target="_blank"
+                    href="https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens#creating-a-personal-access-token-classic"
+                    >Tutorial</a
+                  >
+                  on how to create such a token.
+                </p>
+                <h5>GitLab</h5>
+                <p>
+                  For private GitLab repositories, CloWM needs a Project Access
+                  Token with the <code>read_api</code> scope and at least
+                  <code>Reporter</code> role.<br />
+                  Read this
+                  <a
+                    target="_blank"
+                    href="https://docs.gitlab.com/ee/user/project/settings/project_access_tokens.html#create-a-project-access-token"
+                    >Tutorial</a
+                  >
+                  on how to create such a token.
+                </p>
+                <p>
+                  Select a distant expiration date for both providers to ensure
+                  that there won't be any problems in the short future.
+                </p>
+              </div>
+            </div>
           </div>
         </div>
       </form>
@@ -412,4 +529,8 @@ onMounted(() => {
   </bootstrap-modal>
 </template>
 
-<style scoped></style>
+<style scoped>
+.hover-info:hover {
+  color: var(--bs-info) !important;
+}
+</style>
diff --git a/src/components/workflows/modals/UpdateWorkflowCredentialsModal.vue b/src/components/workflows/modals/UpdateWorkflowCredentialsModal.vue
new file mode 100644
index 0000000000000000000000000000000000000000..df4ddd1b27e32884107fed0d40de7ac16d7d2f6f
--- /dev/null
+++ b/src/components/workflows/modals/UpdateWorkflowCredentialsModal.vue
@@ -0,0 +1,285 @@
+<script setup lang="ts">
+import BootstrapModal from "@/components/modals/BootstrapModal.vue";
+import type { WorkflowCredentialsIn, WorkflowOut } from "@/client/workflow";
+import { WorkflowCredentialsService } from "@/client/workflow";
+import { onMounted, ref, reactive } from "vue";
+import { Modal, Toast } from "bootstrap";
+import FontAwesomeIcon from "@/components/FontAwesomeIcon.vue";
+import DeleteModal from "@/components/modals/DeleteModal.vue";
+import { GitRepository } from "@/utils/GitRepository";
+
+// Constants
+// =============================================================================
+const randomIDSuffix = Math.random().toString(16).substring(2, 8);
+
+// Bootstrap Elements
+// =============================================================================
+let updateCredentialsModal: Modal | null = null;
+let successToast: Toast | null = null;
+
+// Form Elements
+// =============================================================================
+const credentialsUpdateForm = ref<HTMLFormElement | undefined>(undefined);
+const credentialsInputElement = ref<HTMLInputElement | undefined>(undefined);
+
+// Props
+// =============================================================================
+const props = defineProps<{
+  modalID: string;
+  workflow: WorkflowOut;
+}>();
+
+// Reactive State
+// =============================================================================
+const credentials = reactive<WorkflowCredentialsIn>({
+  token: "",
+});
+
+const formState = reactive<{
+  loading: boolean;
+  validated: boolean;
+  updateCredentials: boolean;
+  error: boolean;
+}>({
+  loading: false,
+  validated: false,
+  updateCredentials: true,
+  error: false,
+});
+
+// Events
+// =============================================================================
+const emit = defineEmits<{
+  (
+    e: "credentials-updated",
+    workflow: WorkflowOut,
+    credentialsDeleted: boolean,
+  ): void;
+}>();
+
+// Functions
+// =============================================================================
+function resetForm() {
+  credentials.token = "";
+}
+
+function modalClosed() {
+  formState.validated = false;
+  credentialsInputElement.value?.setCustomValidity("");
+  formState.error = false;
+  resetForm();
+}
+
+function updateCredentials() {
+  formState.validated = true;
+  formState.error = false;
+  credentialsInputElement.value?.setCustomValidity("");
+  if (credentialsUpdateForm.value?.checkValidity()) {
+    const repo = GitRepository.buildRepository(
+      props.workflow.repository_url,
+      props.workflow.versions[0].git_commit_hash,
+      credentials.token,
+    );
+    repo.checkFileExist("main.nf").then((result: boolean) => {
+      if (result) {
+        WorkflowCredentialsService.workflowCredentialsUpdateWorkflowCredentials(
+          props.workflow.workflow_id,
+          credentials,
+        )
+          .then(() => {
+            formState.updateCredentials = true;
+            emit("credentials-updated", props.workflow, false);
+            successToast?.show();
+            updateCredentialsModal?.hide();
+          })
+          .catch((error) => {
+            console.error(error);
+          })
+          .finally(() => {
+            formState.loading = false;
+          });
+      } else {
+        formState.error = true;
+        credentialsInputElement.value?.setCustomValidity(
+          "Can't access repository.",
+        );
+        formState.loading = false;
+      }
+    });
+    formState.loading = true;
+  }
+}
+
+function deleteCredentials() {
+  formState.loading = true;
+  WorkflowCredentialsService.workflowCredentialsDeleteWorkflowCredentials(
+    props.workflow.workflow_id,
+  )
+    .then(() => {
+      formState.updateCredentials = false;
+      emit("credentials-updated", props.workflow, true);
+      successToast?.show();
+      updateCredentialsModal?.hide();
+    })
+    .catch((error) => {
+      console.error(error);
+    })
+    .finally(() => {
+      formState.loading = false;
+    });
+}
+
+// Lifecycle Events
+// =============================================================================
+onMounted(() => {
+  updateCredentialsModal = new Modal("#" + props.modalID);
+  successToast = new Toast("#successToast-" + randomIDSuffix);
+});
+</script>
+
+<template>
+  <DeleteModal
+    v-if="props.workflow.private"
+    :modalID="'delete-credentials-modal' + randomIDSuffix"
+    :object-name-delete="'credentials for workflow ' + props.workflow.name"
+    :back-modal-id="modalID"
+    @confirm-delete="deleteCredentials()"
+  />
+  <div class="toast-container position-fixed top-toast end-0 p-3">
+    <div
+      role="alert"
+      aria-live="assertive"
+      aria-atomic="true"
+      class="toast text-bg-success align-items-center border-0"
+      data-bs-autohide="true"
+      :id="'successToast-' + randomIDSuffix"
+    >
+      <div class="d-flex">
+        <div v-if="formState.updateCredentials" class="toast-body">
+          Successfully updated credentials
+        </div>
+        <div v-else class="toast-body">Successfully deleted credentials</div>
+        <button
+          type="button"
+          class="btn-close btn-close-white me-2 m-auto"
+          data-bs-dismiss="toast"
+          aria-label="Close"
+        ></button>
+      </div>
+    </div>
+  </div>
+  <bootstrap-modal
+    :modalID="modalID"
+    :static-backdrop="true"
+    modal-label="Update Workflow Version Icon Modal"
+    v-on="{ 'hidden.bs.modal': modalClosed }"
+  >
+    <template v-slot:header>
+      Update git repository credentials for Workflow
+      <span class="fw-bold">{{ props.workflow.name }}</span>
+    </template>
+    <template v-slot:extra-button v-if="props.workflow.private">
+      <button
+        class="btn delete-icon"
+        data-bs-toggle="modal"
+        :data-bs-target="'#delete-credentials-modal' + randomIDSuffix"
+      >
+        <font-awesome-icon icon="fa-solid fa-trash" />
+      </button>
+    </template>
+    <template v-slot:body>
+      <form
+        ref="credentialsUpdateForm"
+        id="credentialsUpdateForm"
+        :class="{ 'was-validated': formState.validated }"
+      >
+        <label for="workflowCredentialsInput" class="form-label"
+          >New Token</label
+        >
+        <div class="input-group">
+          <div class="input-group-text">
+            <font-awesome-icon icon="fa-solid fa-key" />
+          </div>
+          <input
+            type="password"
+            ref="credentialsInputElement"
+            class="form-control"
+            id="workflowCredentialsInput"
+            aria-describedby="iconHelp"
+            required
+            v-model="credentials.token"
+          />
+        </div>
+        <div v-if="formState.error" class="text-danger">
+          Can't access the repository.
+        </div>
+        <div class="card card-body mt-3">
+          <h5>GitHub</h5>
+          <p>
+            For private GitHub repositories, CloWM needs a Personal Access Token
+            (classic) with the scope <code>repo</code>.<br />
+            Read this
+            <a
+              target="_blank"
+              href="https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens#creating-a-personal-access-token-classic"
+              >Tutorial</a
+            >
+            on how to create such a token.
+          </p>
+          <h5>GitLab</h5>
+          <p>
+            For private GitLab repositories, CloWM needs a Project Access Token
+            with the <code>read_api</code> scope and at least
+            <code>Reporter</code> role.<br />
+            Read this
+            <a
+              target="_blank"
+              href="https://docs.gitlab.com/ee/user/project/settings/project_access_tokens.html#create-a-project-access-token"
+              >Tutorial</a
+            >
+            on how to create such a token.
+          </p>
+          <p>
+            Select a distant expiration date for both providers to ensure that
+            there won't be any problems in the short future.
+          </p>
+        </div>
+      </form>
+    </template>
+    <template v-slot:footer>
+      <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">
+        Close
+      </button>
+      <button
+        type="submit"
+        form="credentialsUpdateForm"
+        class="btn btn-primary"
+        :disabled="formState.loading"
+        @click.prevent="updateCredentials"
+      >
+        <span
+          v-if="formState.loading"
+          class="spinner-border spinner-border-sm"
+          role="status"
+          aria-hidden="true"
+        ></span>
+        Save
+      </button>
+    </template>
+  </bootstrap-modal>
+</template>
+
+<style scoped>
+img {
+  max-height: 64px;
+  max-width: 64px;
+}
+
+.delete-icon {
+  color: var(--bs-secondary) !important;
+}
+
+.delete-icon:hover {
+  color: var(--bs-danger) !important;
+}
+</style>
diff --git a/src/components/workflows/modals/UpdateWorkflowModal.vue b/src/components/workflows/modals/UpdateWorkflowModal.vue
index 471d884a4e0791401b404e7ae77c2ddfc68ccb84..4ce43dc0026daf6c66241584f25372d8601187d1 100644
--- a/src/components/workflows/modals/UpdateWorkflowModal.vue
+++ b/src/components/workflows/modals/UpdateWorkflowModal.vue
@@ -2,14 +2,14 @@
 import { computed, onMounted, reactive, ref, watch } from "vue";
 import { Modal, Toast } from "bootstrap";
 import type {
-  Body_Workflow_update_workflow,
+  WorkflowUpdate,
   WorkflowOut,
-  WorkflowVersionFull,
+  WorkflowVersion,
+  ApiError,
 } from "@/client/workflow";
 import BootstrapModal from "@/components/modals/BootstrapModal.vue";
 import FontAwesomeIcon from "@/components/FontAwesomeIcon.vue";
-import type { WorkflowVersionReduced, ApiError } from "@/client/workflow";
-import { WorkflowService } from "@/client/workflow";
+import { WorkflowService, WorkflowCredentialsService } from "@/client/workflow";
 import {
   GitRepository,
   requiredRepositoryFiles,
@@ -29,7 +29,7 @@ const workflowUpdateForm = ref<HTMLFormElement | undefined>(undefined);
 const workflowIconInputElement = ref<HTMLInputElement | undefined>(undefined);
 const workflowVersionElement = ref<HTMLInputElement | undefined>(undefined);
 const workflowGitCommitHashElement = ref<HTMLInputElement | undefined>(
-  undefined
+  undefined,
 );
 const workflowIconElement = ref<HTMLImageElement | undefined>(undefined);
 
@@ -46,10 +46,11 @@ const props = defineProps<{
 
 // Reactive State
 // =============================================================================
-const workflowUpdate = reactive<Body_Workflow_update_workflow>({
-  icon: undefined,
+const workflowUpdate = reactive<WorkflowUpdate>({
   version: "",
   git_commit_hash: "",
+  delete_modes: [],
+  append_modes: [],
 });
 
 const formState = reactive<{
@@ -58,58 +59,64 @@ const formState = reactive<{
   loading: boolean;
   checkRepoLoading: boolean;
   allowUpload: boolean;
+  loadCredentials: boolean;
+  workflowToken?: string;
 }>({
   loading: false,
   checkRepoLoading: false,
   allowUpload: false,
   validated: false,
   missingFiles: [],
+  loadCredentials: false,
+  workflowToken: undefined,
 });
 
 watch(
   () => props.workflow,
   () => {
     resetForm();
-  }
+    if (props.workflow.private) {
+      formState.loadCredentials = true;
+      WorkflowCredentialsService.workflowCredentialsGetWorkflowCredentials(
+        props.workflow.workflow_id,
+      )
+        .then((credentials) => {
+          formState.workflowToken = credentials.token ?? undefined;
+        })
+        .catch(() => {
+          formState.workflowToken = undefined;
+        })
+        .finally(() => {
+          formState.loadCredentials = false;
+        });
+    } else {
+      formState.workflowToken = undefined;
+    }
+  },
 );
 
 // Computed Properties
 // =============================================================================
-const latestVersion = computed<WorkflowVersionReduced>(() => {
+const latestVersion = computed<WorkflowVersion>(() => {
   // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
   return calculateLatestVersion(props.workflow.versions)!;
 });
 const gitIcon = computed<string>(() =>
-  determineGitIcon(props.workflow.repository_url)
+  determineGitIcon(props.workflow.repository_url),
 );
 
 const showIcon = computed<boolean>(
-  () =>
-    latestVersion.value.icon_url != undefined ||
-    workflowUpdate.icon != undefined
+  () => latestVersion.value.icon_url != undefined,
 );
 
 // Emitted Events
 // =============================================================================
 const emit = defineEmits<{
-  (e: "workflow-updated", workflow: WorkflowVersionFull): void;
+  (e: "workflow-updated", workflow: WorkflowVersion): void;
 }>();
 
 // Functions
 // =============================================================================
-function iconChanged() {
-  workflowUpdate.icon = workflowIconInputElement.value?.files?.[0].slice();
-  if (workflowUpdate.icon != undefined) {
-    // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
-    workflowIconElement.value!.src = URL.createObjectURL(
-      workflowUpdate.icon.slice()
-    );
-  } else {
-    // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
-    workflowIconElement.value!.src = latestVersion.value.icon_url ?? "";
-  }
-}
-
 function modalClosed() {
   formState.validated = false;
   formState.missingFiles = [];
@@ -120,11 +127,11 @@ function modalClosed() {
 function checkVersionValidity() {
   if (valid(workflowUpdate.version) == null) {
     workflowVersionElement.value?.setCustomValidity(
-      "Please use semantic versioning"
+      "Please use semantic versioning",
     );
   } else if (lte(workflowUpdate.version, latestVersion.value.version)) {
     workflowVersionElement.value?.setCustomValidity(
-      "The new version must be greater than previous version"
+      "The new version must be greater than previous version",
     );
   } else {
     workflowVersionElement.value?.setCustomValidity("");
@@ -133,12 +140,13 @@ function checkVersionValidity() {
 
 function checkRepository() {
   formState.validated = true;
+  workflowGitCommitHashElement.value?.setCustomValidity("");
   if (workflowUpdateForm.value?.checkValidity() && !formState.allowUpload) {
     formState.missingFiles = [];
-    workflowGitCommitHashElement.value?.setCustomValidity("");
     const repo = GitRepository.buildRepository(
       props.workflow.repository_url,
-      workflowUpdate.git_commit_hash
+      workflowUpdate.git_commit_hash,
+      formState.workflowToken,
     );
     repo
       .checkFilesExist(requiredRepositoryFiles, true)
@@ -148,7 +156,7 @@ function checkRepository() {
       .catch((e: Error) => {
         formState.missingFiles = e.message.split(",");
         workflowGitCommitHashElement.value?.setCustomValidity(
-          "Files are missing in the repository"
+          "Files are missing in the repository",
         );
       });
   }
@@ -159,7 +167,6 @@ function resetForm() {
   // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
   workflowIconElement.value!.src = latestVersion.value.icon_url ?? "";
   workflowUpdate.version = "";
-  workflowUpdate.icon = undefined;
   workflowUpdate.git_commit_hash = "";
   if (workflowIconInputElement.value != undefined) {
     workflowIconInputElement.value.value = "";
@@ -174,7 +181,7 @@ function updateWorkflow() {
     workflowGitCommitHashElement.value?.setCustomValidity("");
     WorkflowService.workflowUpdateWorkflow(
       props.workflow.workflow_id,
-      workflowUpdate
+      workflowUpdate,
     )
       .then((version) => {
         emit("workflow-updated", version);
@@ -186,7 +193,7 @@ function updateWorkflow() {
         const errorText = error.body["detail"];
         if (errorText.startsWith("Workflow with git_commit_hash")) {
           workflowGitCommitHashElement.value?.setCustomValidity(
-            "Git commit is already used by a workflow"
+            "Git commit is already used by a workflow",
           );
         }
       })
@@ -250,44 +257,49 @@ onMounted(() => {
             target="_blank"
             >{{ props.workflow.repository_url }}</a
           >
+          <font-awesome-icon
+            v-if="props.workflow.private"
+            class="ms-2"
+            icon="fa-solid fa-lock"
+          />
           <img
-            :src="latestVersion.icon_url"
+            :src="latestVersion.icon_url ?? undefined"
             ref="workflowIconElement"
             class="float-end"
             :hidden="!showIcon"
           />
         </div>
-        <div class="mb-3">
-          <label for="workflowGitCommitInput" class="form-label"
-            >Git Commit Hash</label
-          >
-          <div class="input-group">
-            <div class="input-group-text">
-              <font-awesome-icon icon="fa-solid fa-code-commit" />
+        <div class="row">
+          <div class="col-8">
+            <label for="workflowGitCommitInput" class="form-label"
+              >Git Commit Hash</label
+            >
+            <div class="input-group">
+              <div class="input-group-text">
+                <font-awesome-icon icon="fa-solid fa-code-commit" />
+              </div>
+              <input
+                type="text"
+                class="form-control text-lowercase"
+                id="workflowGitCommitInput"
+                placeholder="ba8bcd9..."
+                required
+                ref="workflowGitCommitHashElement"
+                maxlength="40"
+                pattern="[0-9a-f]{40}"
+                v-model="workflowUpdate.git_commit_hash"
+                @change="formState.allowUpload = false"
+              />
+            </div>
+            <div v-if="formState.missingFiles.length > 0" class="text-danger">
+              The following files are missing in the repository
+              <ul>
+                <li v-for="file in formState.missingFiles" :key="file">
+                  {{ file }}
+                </li>
+              </ul>
             </div>
-            <input
-              type="text"
-              class="form-control text-lowercase"
-              id="workflowGitCommitInput"
-              placeholder="ba8bcd9..."
-              required
-              ref="workflowGitCommitHashElement"
-              maxlength="40"
-              pattern="[0-9a-f]{40}"
-              v-model="workflowUpdate.git_commit_hash"
-              @change="formState.allowUpload = false"
-            />
           </div>
-        </div>
-        <div v-if="formState.missingFiles.length > 0" class="text-danger">
-          The following files are missing in the repository
-          <ul>
-            <li v-for="file in formState.missingFiles" :key="file">
-              {{ file }}
-            </li>
-          </ul>
-        </div>
-        <div class="row mb-3">
           <div class="col-4">
             <label for="workflowVersionInput" class="form-label">Version</label>
             <div class="input-group">
@@ -311,23 +323,6 @@ onMounted(() => {
               Previous Version: {{ latestVersion.version }}
             </div>
           </div>
-          <div class="col-8">
-            <label for="workflowIconInput" class="form-label"
-              >Optional Icon</label
-            >
-            <input
-              type="file"
-              ref="workflowIconInputElement"
-              accept="image/*"
-              class="form-control"
-              id="workflowIconInput"
-              @change="iconChanged"
-              aria-describedby="iconHelp"
-            />
-            <div id="iconHelp" class="form-text">
-              If not set, the previous icon will be used
-            </div>
-          </div>
         </div>
       </form>
     </template>
@@ -336,7 +331,7 @@ onMounted(() => {
         type="button"
         class="btn btn-info me-auto"
         @click="checkRepository"
-        :disabled="formState.allowUpload"
+        :disabled="formState.allowUpload || formState.loadCredentials"
       >
         <span
           v-if="formState.checkRepoLoading"
diff --git a/src/components/workflows/modals/UpdateWorkflowVersionIconModal.vue b/src/components/workflows/modals/UpdateWorkflowVersionIconModal.vue
new file mode 100644
index 0000000000000000000000000000000000000000..ede37a29532718aecfadf5b619851b7f0ab300cf
--- /dev/null
+++ b/src/components/workflows/modals/UpdateWorkflowVersionIconModal.vue
@@ -0,0 +1,262 @@
+<script setup lang="ts">
+import BootstrapModal from "@/components/modals/BootstrapModal.vue";
+import type { WorkflowVersion } from "@/client/workflow";
+import { WorkflowVersionService } from "@/client/workflow";
+import { onMounted, ref, reactive, computed } from "vue";
+import { Modal, Toast } from "bootstrap";
+import FontAwesomeIcon from "@/components/FontAwesomeIcon.vue";
+import DeleteModal from "@/components/modals/DeleteModal.vue";
+
+// Constants
+// =============================================================================
+const randomIDSuffix = Math.random().toString(16).substring(2, 8);
+
+// Bootstrap Elements
+// =============================================================================
+let updateIconModal: Modal | null = null;
+let successToast: Toast | null = null;
+
+// Form Elements
+// =============================================================================
+const iconUpdateForm = ref<HTMLFormElement | undefined>(undefined);
+const iconElement = ref<HTMLImageElement | undefined>(undefined);
+const iconInputElement = ref<HTMLInputElement | undefined>(undefined);
+
+// Props
+// =============================================================================
+const props = defineProps<{
+  modalID: string;
+  version: WorkflowVersion;
+  workflowName?: string;
+}>();
+
+// Reactive State
+// =============================================================================
+const iconUpdate = reactive<{
+  icon: Blob | null;
+}>({
+  icon: null,
+});
+
+const formState = reactive<{
+  loading: boolean;
+  validated: boolean;
+  uploadIcon: boolean;
+}>({
+  loading: false,
+  validated: false,
+  uploadIcon: true,
+});
+
+// Computed Properties
+// =============================================================================
+const showIcon = computed<boolean>(
+  () => props.version.icon_url != undefined || iconUpdate.icon != undefined,
+);
+
+// Events
+// =============================================================================
+const emit = defineEmits<{
+  (e: "icon-updated", version: WorkflowVersion, icon_url: string): void;
+  (e: "icon-deleted", version: WorkflowVersion): void;
+}>();
+
+// Functions
+// =============================================================================
+function resetForm() {
+  // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+  iconElement.value!.src = props.version.icon_url ?? "";
+  if (iconInputElement.value != undefined) {
+    iconInputElement.value.value = "";
+  }
+}
+
+function modalClosed() {
+  formState.validated = false;
+  resetForm();
+}
+
+function iconChanged() {
+  iconUpdate.icon = iconInputElement.value?.files?.[0].slice() ?? null;
+  if (iconUpdate.icon != null) {
+    // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+    iconElement.value!.src = URL.createObjectURL(iconUpdate.icon.slice());
+  } else {
+    // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+    iconElement.value!.src = props.version.icon_url ?? "";
+  }
+}
+
+function updateIcon() {
+  formState.validated = true;
+  if (iconUpdateForm.value?.checkValidity() && iconUpdate.icon != null) {
+    formState.loading = true;
+    WorkflowVersionService.workflowVersionUploadWorkflowVersionIcon(
+      props.version.workflow_id,
+      props.version.git_commit_hash,
+      {
+        icon: iconUpdate.icon,
+      },
+    )
+      .then((response) => {
+        formState.uploadIcon = true;
+        emit("icon-updated", props.version, response.icon_url);
+        successToast?.show();
+        updateIconModal?.hide();
+      })
+      .catch((error) => {
+        console.error(error);
+      })
+      .finally(() => {
+        formState.loading = false;
+      });
+  }
+}
+
+function deleteIcon() {
+  formState.loading = true;
+  WorkflowVersionService.workflowVersionDeleteWorkflowVersionIcon(
+    props.version.workflow_id,
+    props.version.git_commit_hash,
+  )
+    .then(() => {
+      formState.uploadIcon = false;
+      emit("icon-deleted", props.version);
+      successToast?.show();
+      updateIconModal?.hide();
+    })
+    .catch((error) => {
+      console.error(error);
+    })
+    .finally(() => {
+      formState.loading = false;
+    });
+}
+
+// Lifecycle Events
+// =============================================================================
+onMounted(() => {
+  updateIconModal = new Modal("#" + props.modalID);
+  successToast = new Toast("#successToast-" + randomIDSuffix);
+});
+</script>
+
+<template>
+  <DeleteModal
+    v-if="props.version.icon_url"
+    :modalID="'delete-icon-modal' + randomIDSuffix"
+    :object-name-delete="'icon for workflow ' + props.workflowName"
+    :back-modal-id="modalID"
+    @confirm-delete="deleteIcon()"
+  />
+  <div class="toast-container position-fixed top-toast end-0 p-3">
+    <div
+      role="alert"
+      aria-live="assertive"
+      aria-atomic="true"
+      class="toast text-bg-success align-items-center border-0"
+      data-bs-autohide="true"
+      :id="'successToast-' + randomIDSuffix"
+    >
+      <div class="d-flex">
+        <div v-if="formState.uploadIcon" class="toast-body">
+          Successfully uploaded icon
+        </div>
+        <div v-else class="toast-body">Successfully deleted icon</div>
+        <button
+          type="button"
+          class="btn-close btn-close-white me-2 m-auto"
+          data-bs-dismiss="toast"
+          aria-label="Close"
+        ></button>
+      </div>
+    </div>
+  </div>
+  <bootstrap-modal
+    :modalID="modalID"
+    :static-backdrop="true"
+    modal-label="Update Workflow Version IconModal"
+    v-on="{ 'hidden.bs.modal': modalClosed }"
+  >
+    <template v-slot:header>
+      Update Icon Workflow
+      <span class="fw-bold"
+        >{{ props.workflowName }}@{{ props.version.version }}</span
+      >
+    </template>
+    <template v-slot:extra-button v-if="props.version.icon_url">
+      <button
+        class="btn delete-icon"
+        data-bs-toggle="modal"
+        :data-bs-target="'#delete-icon-modal' + randomIDSuffix"
+      >
+        <font-awesome-icon icon="fa-solid fa-trash" />
+      </button>
+    </template>
+    <template v-slot:body>
+      <form
+        ref="iconUpdateForm"
+        id="iconUpdateForm"
+        :class="{ 'was-validated': formState.validated }"
+      >
+        <div class="row">
+          <div class="col-10">
+            <label for="workflowIconInput" class="form-label">New Icon</label>
+            <input
+              type="file"
+              ref="iconInputElement"
+              accept="image/*"
+              class="form-control"
+              id="workflowIconInput"
+              @change="iconChanged"
+              aria-describedby="iconHelp"
+              required
+            />
+          </div>
+          <div class="col-2">
+            <img
+              :src="props.version.icon_url ?? undefined"
+              ref="iconElement"
+              :hidden="!showIcon"
+            />
+          </div>
+        </div>
+      </form>
+    </template>
+    <template v-slot:footer>
+      <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">
+        Close
+      </button>
+      <button
+        type="submit"
+        form="iconUpdateForm"
+        class="btn btn-primary"
+        :disabled="formState.loading"
+        @click.prevent="updateIcon"
+      >
+        <span
+          v-if="formState.loading"
+          class="spinner-border spinner-border-sm"
+          role="status"
+          aria-hidden="true"
+        ></span>
+        Save
+      </button>
+    </template>
+  </bootstrap-modal>
+</template>
+
+<style scoped>
+img {
+  max-height: 64px;
+  max-width: 64px;
+}
+
+.delete-icon {
+  color: var(--bs-secondary) !important;
+}
+
+.delete-icon:hover {
+  color: var(--bs-danger) !important;
+}
+</style>
diff --git a/src/main.ts b/src/main.ts
index ccbaf6cb953600152661909bbee71d6490166e61..a0b5f354ab8ee7e03f16e833b20ae49ace12ea6f 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -12,9 +12,14 @@ import dayjs from "dayjs";
 import relativeTime from "dayjs/plugin/relativeTime"; // import plugin
 import isSameOrBefore from "dayjs/plugin/isSameOrBefore";
 import "dayjs/locale/en-gb";
+import utc from "dayjs/plugin/utc";
+import timezone from "dayjs/plugin/timezone";
 
 dayjs.extend(relativeTime); // use plugin
 dayjs.extend(isSameOrBefore);
+dayjs.extend(utc);
+dayjs.extend(timezone);
+dayjs.tz.setDefault(dayjs.tz.guess());
 
 import "bootstrap/dist/css/bootstrap.css";
 import "@fortawesome/fontawesome-free/css/fontawesome.css";
diff --git a/src/router/index.ts b/src/router/index.ts
index 8abd1c20ea0b19fd0cc8c0381e86aeb4e2e7fcc2..cd6d25646eb4b2ff556af43ba1b667106b4e973a 100644
--- a/src/router/index.ts
+++ b/src/router/index.ts
@@ -60,6 +60,7 @@ const router = createRouter({
           props: (route) => ({
             repository: route.query.repository,
             commit_hash: route.query.commit_hash,
+            token: route.query.token,
           }),
         },
         {
diff --git a/src/stores/auth.ts b/src/stores/auth.ts
index 0022c43a9ff8c467f34ded8d69b94349275d0102..ddb85473ab9af1e3f6a6b547e6012e8396ae2571 100644
--- a/src/stores/auth.ts
+++ b/src/stores/auth.ts
@@ -31,7 +31,7 @@ function parseJwt(token: string): DecodedToken {
       .map(function (c) {
         return "%" + ("00" + c.charCodeAt(0).toString(16)).slice(-2);
       })
-      .join("")
+      .join(""),
   );
 
   return JSON.parse(jsonPayload) as DecodedToken;
@@ -45,7 +45,7 @@ export const useAuthStore = defineStore({
       decodedToken: null,
       user: null,
       s3key: null,
-    } as RootState),
+    }) as RootState,
   getters: {
     roles(): string[] {
       return (
diff --git a/src/stores/buckets.ts b/src/stores/buckets.ts
index a884d9f97cfd5a78cc3698679bbc20a702346993..22f91f79fc65734335413af8e72158d5c70ded7f 100644
--- a/src/stores/buckets.ts
+++ b/src/stores/buckets.ts
@@ -21,12 +21,12 @@ export const useBucketStore = defineStore({
       ownPermissions: {},
       _lastFetchBucketPromise: undefined,
       _lastFetchBucketPromiseTimestamp: 0,
-    } as {
+    }) as {
       buckets: BucketOut[];
       ownPermissions: Record<string, BucketPermissionOut>;
       _lastFetchBucketPromise?: CancelablePromise<never>;
       _lastFetchBucketPromiseTimestamp: number;
-    }),
+    },
   getters: {
     ownBucketsAndFullPermissions(): string[] {
       const authStore = useAuthStore();
@@ -36,7 +36,7 @@ export const useBucketStore = defineStore({
         .concat(
           Object.values(this.ownPermissions)
             .filter((perm) => perm.permission === Permission.READWRITE)
-            .map((perm) => perm.bucket_name)
+            .map((perm) => perm.bucket_name),
         );
       names.sort();
       return names;
@@ -71,7 +71,7 @@ export const useBucketStore = defineStore({
       });
     },
     getBucketPermission(): (
-      bucketName: string
+      bucketName: string,
     ) => BucketPermissionOut | undefined {
       return (bucketName) => this.ownPermissions[bucketName];
     },
@@ -120,12 +120,12 @@ export const useBucketStore = defineStore({
         | undefined = null,
       // eslint-disable-next-line @typescript-eslint/no-explicit-any
       onRejected: ((reason: any) => void) | null | undefined = null,
-      onFinally: (() => void) | null | undefined = null
+      onFinally: (() => void) | null | undefined = null,
     ) {
       const authStore = useAuthStore();
       if (authStore.authenticated && !authStore.foreignUser) {
         BucketPermissionService.bucketPermissionListPermissionsPerUser(
-          authStore.currentUID
+          authStore.currentUID,
         )
           .then((permissions) => {
             const new_permissions: Record<string, BucketPermissionOut> = {};
@@ -141,7 +141,7 @@ export const useBucketStore = defineStore({
     },
     deleteOwnPermission(bucketName: string) {
       this.buckets = this.buckets.filter(
-        (bucket) => bucket.name !== bucketName
+        (bucket) => bucket.name !== bucketName,
       );
       delete this.ownPermissions[bucketName];
     },
@@ -149,7 +149,7 @@ export const useBucketStore = defineStore({
       onFulfilled: ((buckets: BucketOut[]) => void) | null | undefined = null,
       // eslint-disable-next-line @typescript-eslint/no-explicit-any
       onRejected: ((reason: any) => void) | null | undefined = null,
-      onFinally: (() => void) | null | undefined = null
+      onFinally: (() => void) | null | undefined = null,
     ) {
       /* If the time between two calls to this function is less than 5 seconds,
        * then no API call will be made and the last one reused
@@ -179,13 +179,13 @@ export const useBucketStore = defineStore({
       onFulfilled: ((bucket: BucketOut) => void) | null | undefined = null,
       // eslint-disable-next-line @typescript-eslint/no-explicit-any
       onRejected: ((reason: any) => void) | null | undefined = null,
-      onFinally: (() => void) | null | undefined = null
+      onFinally: (() => void) | null | undefined = null,
     ) {
       BucketService.bucketGetBucket(bucketName)
         .then((bucket) => {
           this.buckets[
             this.buckets.findIndex(
-              (tempBucket) => tempBucket.name === bucket.name
+              (tempBucket) => tempBucket.name === bucket.name,
             )
           ] = bucket;
           onFulfilled?.(bucket);
@@ -198,12 +198,12 @@ export const useBucketStore = defineStore({
       onFulfilled: (() => void) | null | undefined = null,
       // eslint-disable-next-line @typescript-eslint/no-explicit-any
       onRejected: ((reason: any) => void) | null | undefined = null,
-      onFinally: (() => void) | null | undefined = null
+      onFinally: (() => void) | null | undefined = null,
     ) {
       BucketService.bucketDeleteBucket(bucketName, true)
         .then(() => {
           this.buckets = this.buckets.filter(
-            (bucket) => bucket.name !== bucketName
+            (bucket) => bucket.name !== bucketName,
           );
           onFulfilled?.();
         })
@@ -218,13 +218,13 @@ export const useBucketStore = defineStore({
         | undefined = null,
       // eslint-disable-next-line @typescript-eslint/no-explicit-any
       onRejected: ((reason: any) => void) | null | undefined = null,
-      onFinally: (() => void) | null | undefined = null
+      onFinally: (() => void) | null | undefined = null,
     ) {
       BucketService.bucketCreateBucket(bucket)
         .then((createdBucket) => {
           this.buckets.push(createdBucket);
           this.buckets.sort((bucketA, bucketB) =>
-            bucketA.name >= bucketB.name ? 1 : -1
+            bucketA.name >= bucketB.name ? 1 : -1,
           );
           onFulfilled?.(createdBucket);
         })
diff --git a/src/utils/DayjsAdapter.ts b/src/utils/DayjsAdapter.ts
index 086b6d0b092545f86a362faad8ab98ae6c42d9f2..fbfd8bf6edcce583a130170cb4697b0313275240 100644
--- a/src/utils/DayjsAdapter.ts
+++ b/src/utils/DayjsAdapter.ts
@@ -78,7 +78,7 @@ _adapters._date.override({
     // eslint-disable-next-line @typescript-eslint/no-explicit-any
     time: any,
     unit: (TimeUnit & QUnitType) | "isoWeek",
-    weekday?: number
+    weekday?: number,
   ) {
     if (unit === "isoWeek") {
       // Ensure that weekday has a valid format
diff --git a/src/utils/GitRepository.ts b/src/utils/GitRepository.ts
index 697d6963e3d945722115768d35a7fd299ef2c1ea..68004e6c06e19000f3710ff2db33f8430c200301 100644
--- a/src/utils/GitRepository.ts
+++ b/src/utils/GitRepository.ts
@@ -1,4 +1,5 @@
 import axios from "axios";
+import type { AxiosInstance, AxiosBasicCredentials } from "axios";
 
 export const requiredRepositoryFiles = [
   "main.nf",
@@ -26,13 +27,27 @@ export function determineGitIcon(repo_url?: string): string {
 export abstract class GitRepository {
   protected repo: URL;
   protected gitCommitHash: string;
-  protected constructor(repoUrl: string, gitCommitHash: string) {
+  protected token?: string;
+  protected httpClient: AxiosInstance;
+
+  protected constructor(
+    repoUrl: string,
+    gitCommitHash: string,
+    token?: string,
+  ) {
     this.repo = new URL(repoUrl);
     this.gitCommitHash = gitCommitHash;
+    if (token != undefined && token.length > 0) {
+      this.token = token;
+    }
+    this.httpClient = axios.create({
+      maxRedirects: 2,
+    });
   }
+
   async checkFileExist(filepath: string): Promise<boolean> {
     try {
-      await axios.head(this.fileUrl(filepath));
+      await this.httpClient.head(this.fileUrl(filepath));
     } catch (e) {
       return false;
     }
@@ -42,7 +57,7 @@ export abstract class GitRepository {
   // eslint-disable-next-line @typescript-eslint/no-explicit-any
   async downloadFile(filepath: string): Promise<any> {
     try {
-      return await axios.get(this.fileUrl(filepath));
+      return await this.httpClient.get(await this.downloadFileUrl(filepath));
     } catch (e) {
       return "";
     }
@@ -50,10 +65,10 @@ export abstract class GitRepository {
 
   async checkFilesExist(
     files: string[],
-    raiseError = false
+    raiseError = false,
   ): Promise<boolean[]> {
     const checks = Promise.all(
-      files.map((filepath) => this.checkFileExist(filepath))
+      files.map((filepath) => this.checkFileExist(filepath)),
     );
     const results = await checks;
     if (raiseError) {
@@ -64,16 +79,20 @@ export abstract class GitRepository {
     }
     return results;
   }
+
   protected abstract fileUrl(filepath: string): string;
 
+  protected abstract downloadFileUrl(filepath: string): Promise<string>;
+
   static buildRepository(
     repoUrl: string,
-    gitCommitHash: string
+    gitCommitHash: string,
+    token?: string,
   ): GitRepository {
     if (repoUrl.includes("github")) {
-      return new GithubRepository(repoUrl, gitCommitHash);
+      return new GithubRepository(repoUrl, gitCommitHash, token);
     } else if (repoUrl.includes("gitlab")) {
-      return new GitlabRepository(repoUrl, gitCommitHash);
+      return new GitlabRepository(repoUrl, gitCommitHash, token);
     }
     throw new Error("Repository is not supported.");
   }
@@ -82,31 +101,80 @@ export abstract class GitRepository {
 class GithubRepository extends GitRepository {
   private readonly account: string;
   private readonly repoName: string;
-  constructor(repoUrl: string, gitCommitHash: string) {
-    super(repoUrl, gitCommitHash);
+
+  constructor(repoUrl: string, gitCommitHash: string, token?: string) {
+    super(repoUrl, gitCommitHash, token);
     const pathParts = this.repo.pathname.slice(1).split("/");
     this.account = pathParts[0];
     this.repoName = pathParts[1];
+    if (token) {
+      this.httpClient.interceptors.request.use((req) => {
+        req.auth = {
+          password: this.token,
+          username: this.account,
+        } as AxiosBasicCredentials;
+        return req;
+      });
+    }
+    this.httpClient.interceptors.request.use((req) => {
+      req.headers.setAccept("application/vnd.github.object+json");
+      req.headers["X-GitHub-Api-Version"] = "2022-11-28";
+      return req;
+    });
   }
 
   fileUrl(filepath: string): string {
-    return `https://raw.githubusercontent.com/${this.account}/${this.repoName}/${this.gitCommitHash}/${filepath}`;
+    return `https://api.github.com/repos/${this.account}/${
+      this.repoName
+    }/contents/${encodeURIComponent(filepath)}?ref=${encodeURIComponent(
+      this.gitCommitHash,
+    )}`;
+  }
+
+  protected async downloadFileUrl(filepath: string): Promise<string> {
+    if (this.token != undefined) {
+      return Promise.resolve(
+        `https://raw.githubusercontent.com/${this.account}/${this.repoName}/${this.gitCommitHash}/${filepath}`,
+      );
+    }
+    return (await this.httpClient.get(this.fileUrl(filepath))).data[
+      "download_url"
+    ];
   }
 }
 
 class GitlabRepository extends GitRepository {
-  private readonly account: string[];
-  private readonly repoName: string;
-  constructor(repoUrl: string, gitCommitHash: string) {
-    super(repoUrl, gitCommitHash);
-    const pathParts = this.repo.pathname.slice(1).split("/");
-    this.account = pathParts.slice(0, pathParts.length - 2);
-    this.repoName = pathParts[pathParts.length - 1];
+  private readonly host: string;
+  private readonly project: string;
+
+  constructor(repoUrl: string, gitCommitHash: string, token?: string) {
+    super(repoUrl, gitCommitHash, token);
+    const url = new URL(repoUrl);
+    this.host = url.host;
+    this.project = url.pathname.slice(1);
+    if (token != undefined) {
+      this.httpClient.interceptors.request.use((req) => {
+        req.headers.setAuthorization(`Bearer ${token}`);
+        return req;
+      });
+    }
   }
 
   fileUrl(filepath: string): string {
-    return `https://${this.repo.host}/${this.account.reduce((a, b) =>
-      a.concat(`/${b}`)
-    )}/${this.repoName}/-/raw/${this.gitCommitHash}/${filepath}`;
+    return `https://${this.host}/api/v4/projects/${encodeURIComponent(
+      this.project,
+    )}/repository/files/${encodeURIComponent(
+      filepath,
+    )}?ref=${encodeURIComponent(this.gitCommitHash)}`;
+  }
+
+  protected downloadFileUrl(filepath: string): Promise<string> {
+    return Promise.resolve(
+      `https://${this.host}/api/v4/projects/${encodeURIComponent(
+        this.project,
+      )}/repository/files/${encodeURIComponent(
+        filepath,
+      )}/raw?ref=${encodeURIComponent(this.gitCommitHash)}`,
+    );
   }
 }
diff --git a/src/utils/Workflow.ts b/src/utils/Workflow.ts
index c37cab3b551cd8cf4b95597d3304910b83587b4c..9257886a74986eebd6e7bd1da9ed3a3e17bc4fd2 100644
--- a/src/utils/Workflow.ts
+++ b/src/utils/Workflow.ts
@@ -1,37 +1,27 @@
-import type {
-  WorkflowVersionReduced,
-  WorkflowVersionFull,
-} from "@/client/workflow";
-import dayjs from "dayjs";
+import type { WorkflowVersion } from "@/client/workflow";
 
-export function sortedVersions(
-  versions: WorkflowVersionFull[]
-): WorkflowVersionFull[];
-export function sortedVersions(
-  versions: WorkflowVersionReduced[]
-): WorkflowVersionReduced[];
+export function sortedVersions(versions: WorkflowVersion[]): WorkflowVersion[];
 
-// eslint-disable-next-line @typescript-eslint/no-explicit-any
-export function sortedVersions(versions: any[], desc = true): any[] {
+export function sortedVersions(
+  versions: WorkflowVersion[],
+  desc = true,
+): WorkflowVersion[] {
   const vs = [...versions];
   if (desc) {
-    vs.sort((a, b) => (dayjs(a.created_at).isBefore(b.created_at) ? 1 : -1));
+    vs.sort((a, b) => (a.created_at < b.created_at ? 1 : -1));
   } else {
-    vs.sort((a, b) => (dayjs(a.created_at).isBefore(b.created_at) ? -1 : 1));
+    vs.sort((a, b) => (a.created_at < b.created_at ? -1 : 1));
   }
   return vs;
 }
 
 export function latestVersion(
-  versions: WorkflowVersionFull[]
-): WorkflowVersionFull | undefined;
+  versions: WorkflowVersion[],
+): WorkflowVersion | undefined;
 
 export function latestVersion(
-  versions: WorkflowVersionReduced[]
-): WorkflowVersionReduced | undefined;
-
-// eslint-disable-next-line @typescript-eslint/no-explicit-any
-export function latestVersion(versions: any[]): any | undefined {
+  versions: WorkflowVersion[],
+): WorkflowVersion | undefined {
   if (versions == undefined || versions.length == 0) {
     return undefined;
   }
diff --git a/src/views/object-storage/BucketView.vue b/src/views/object-storage/BucketView.vue
index 1160460a50734cc7a674fdbc7bcb35fb5f1d8bf7..02296fc30729ddf79389cb55acca6d4504b2fc81 100644
--- a/src/views/object-storage/BucketView.vue
+++ b/src/views/object-storage/BucketView.vue
@@ -152,7 +152,7 @@ watch(
       updateObjects(newBucketName);
       objectState.filterString = "";
     }
-  }
+  },
 );
 
 // Computed Properties
@@ -161,17 +161,12 @@ const filteredObjects = computed<(S3ObjectWithFolder | S3PseudoFolder)[]>(
   () => {
     return objectState.filterString.length > 0
       ? visibleObjects.value.filter((obj) =>
-          obj.key.includes(objectState.filterString)
+          obj.key.includes(objectState.filterString),
         )
       : visibleObjects.value;
-  }
+  },
 );
 
-const bucketNameWithoutTenant = computed<string>(() => {
-  const splittedBucketName = props.bucketName.split(":");
-  return splittedBucketName[splittedBucketName.length - 1];
-});
-
 const folderStructure = computed<FolderTree>(() => {
   /**
    * Store the entire folder structure in a bucket in a tree-like data structure
@@ -190,7 +185,7 @@ const folderStructure = computed<FolderTree>(() => {
           // If the sub folder doesn't exist yet, create it
           if (
             Object.keys(currentFolder.subFolders).find(
-              (subFolderName) => subFolderName === folderName
+              (subFolderName) => subFolderName === folderName,
             ) == undefined
           ) {
             currentFolder.subFolders[folderName] = {
@@ -210,7 +205,7 @@ const folderStructure = computed<FolderTree>(() => {
     {
       subFolders: {},
       files: [],
-    } as FolderTree
+    } as FolderTree,
   );
 });
 
@@ -264,10 +259,10 @@ const visibleObjects = computed<(S3ObjectWithFolder | S3PseudoFolder)[]>(() => {
   arr.push(
     ...Object.keys(currentFolder.subFolders).map((subFolderName) => {
       const folderSize = calculateFolderSize(
-        currentFolder.subFolders[subFolderName]
+        currentFolder.subFolders[subFolderName],
       );
       const folderLastModified = dayjs(
-        calculateFolderLastModified(currentFolder.subFolders[subFolderName])
+        calculateFolderLastModified(currentFolder.subFolders[subFolderName]),
       ).toISOString();
       return {
         name: subFolderName,
@@ -276,22 +271,22 @@ const visibleObjects = computed<(S3ObjectWithFolder | S3PseudoFolder)[]>(() => {
         parentFolder: currentSubFolders.value,
         last_modified: folderLastModified,
       } as S3PseudoFolder;
-    })
+    }),
   );
   return arr.filter((obj) => !obj.key.endsWith("/") && obj.key.length > 0);
 });
 
 const subFolderInUrl = computed<boolean>(
-  () => currentSubFolders.value.length > 0
+  () => currentSubFolders.value.length > 0,
 );
 const errorLoadingObjects = computed<boolean>(
-  () => objectState.bucketPermissionError || objectState.bucketNotFoundError
+  () => objectState.bucketPermissionError || objectState.bucketNotFoundError,
 );
 const writableBucket = computed<boolean>(() =>
-  bucketRepository.writableBucket(props.bucketName)
+  bucketRepository.writableBucket(props.bucketName),
 );
 const readableBucket = computed<boolean>(() =>
-  bucketRepository.readableBucket(props.bucketName)
+  bucketRepository.readableBucket(props.bucketName),
 );
 
 // Lifecycle Hooks
@@ -301,7 +296,7 @@ onMounted(() => {
   document
     .querySelectorAll(".tooltip-container")
     .forEach(
-      (tooltipTriggerEl) => new Tooltip(tooltipTriggerEl, { trigger: "hover" })
+      (tooltipTriggerEl) => new Tooltip(tooltipTriggerEl, { trigger: "hover" }),
     );
   successToast = new Toast("#successToast-" + randomIDSuffix);
 });
@@ -333,11 +328,11 @@ function calculateFolderLastModified(folder: FolderTree): string {
     .map((f) => dayjs(f.last_modified))
     .reduce(
       (acc, fileAccessed) => (fileAccessed.isAfter(acc) ? fileAccessed : acc),
-      dayjs("2000-01-01")
+      dayjs("2000-01-01"),
     );
   for (const subFolderName of Object.keys(folder.subFolders)) {
     const lastModifiedSubFolder = dayjs(
-      calculateFolderLastModified(folder.subFolders[subFolderName])
+      calculateFolderLastModified(folder.subFolders[subFolderName]),
     );
     if (lastModifiedSubFolder.isAfter(lastModified)) {
       lastModified = lastModifiedSubFolder;
@@ -354,10 +349,7 @@ function updateObjects(bucketName: string) {
   objectState.bucketNotFoundError = false;
   objectState.bucketPermissionError = false;
   objectState.loading = true;
-  const splittedBucketName = bucketName.split(":");
-  ObjectService.objectGetBucketObjects(
-    splittedBucketName[splittedBucketName.length - 1]
-  )
+  ObjectService.objectGetBucketObjects(bucketName)
     .then((objs) => {
       objectState.objects = objs;
     })
@@ -374,7 +366,7 @@ function updateObjects(bucketName: string) {
 }
 
 function isS3Object(
-  obj: S3PseudoFolder | S3ObjectWithFolder
+  obj: S3PseudoFolder | S3ObjectWithFolder,
 ): obj is S3ObjectWithFolder {
   return (obj as S3ObjectWithFolder).folder !== undefined;
 }
@@ -386,7 +378,7 @@ function isS3Object(
 function objectUploaded(newObject: S3ObjectMetaInformation) {
   bucketRepository.fetchBucket(newObject.bucket);
   const index = objectState.objects.findIndex(
-    (obj) => obj.key === newObject.key
+    (obj) => obj.key === newObject.key,
   );
   if (index > -1) {
     objectState.objects[index] = newObject;
@@ -428,7 +420,7 @@ function confirmedDeleteObject(key: string) {
       deleteObjectsState.deletedItem = splittedKey[splittedKey.length - 1];
       successToast?.show();
       objectState.objects = objectState.objects.filter(
-        (obj) => obj.key !== key
+        (obj) => obj.key !== key,
       );
     })
     .catch((err) => {
@@ -484,7 +476,7 @@ function confirmedDeleteFolder(folderPath: string) {
       deleteObjectsState.deletedItem = splittedPath[splittedPath.length - 2];
       successToast?.show();
       objectState.objects = objectState.objects.filter(
-        (obj) => !obj.key.startsWith(folderPath)
+        (obj) => !obj.key.startsWith(folderPath),
       );
     })
     .catch((err) => {
@@ -509,7 +501,7 @@ watch(
       }, 500);
     }
   },
-  { flush: "post" }
+  { flush: "post" },
 );
 </script>
 
@@ -556,9 +548,9 @@ watch(
             name: 'bucket',
             params: { bucketName: props.bucketName, subFolders: [] },
           }"
-          >{{ bucketNameWithoutTenant }}
+          >{{ props.bucketName }}
         </router-link>
-        <span v-else class="text-secondary">{{ bucketNameWithoutTenant }}</span>
+        <span v-else class="text-secondary">{{ props.bucketName }}</span>
       </li>
       <li
         class="breadcrumb-item"
@@ -788,7 +780,7 @@ watch(
                 >{{ dayjs(obj.last_modified).fromNow() }}</span
               >
             </td>
-            <td>{{ filesize(obj.size) }}</td>
+            <td>{{ filesize(obj.size, { base: 2, standard: "jedec" }) }}</td>
             <!-- Show buttons with dropdown menu if row is an object -->
             <td class="text-end">
               <div
@@ -874,7 +866,7 @@ watch(
                   data-bs-target="#delete-object-modal"
                   @click="
                     deleteFolder(
-                      obj.parentFolder.concat(['']).join('/') + obj.name + '/'
+                      obj.parentFolder.concat(['']).join('/') + obj.name + '/',
                     )
                   "
                 >
diff --git a/src/views/object-storage/BucketsView.vue b/src/views/object-storage/BucketsView.vue
index 89d1bc520d36e6576faf48930e0f889aa68ba076..749e75a70929c4970afb275dbde8fc2981159e15 100644
--- a/src/views/object-storage/BucketsView.vue
+++ b/src/views/object-storage/BucketsView.vue
@@ -35,7 +35,7 @@ function fetchBuckets() {
 const filteredBuckets = computed<BucketOut[]>(() => {
   return bucketsState.filterString.length > 0
     ? bucketRepository.buckets.filter((bucket) =>
-        bucket.name.includes(bucketsState.filterString)
+        bucket.name.includes(bucketsState.filterString),
       )
     : bucketRepository.buckets;
 });
@@ -149,10 +149,11 @@ onMounted(() => {
             :bucket="{
               name: '',
               description: '',
-              created_at: '',
+              created_at: 0,
               owner: '',
               size: 0,
               num_objects: 0,
+              owner_constraint: null,
             }"
           ></bucket-list-item>
         </div>
diff --git a/src/views/object-storage/S3KeyView.vue b/src/views/object-storage/S3KeyView.vue
index ed77ed0eb831e1a68372ff0576caacf35ef09059..4366ee644178f1b00e01dd10d6150d046dc3945e 100644
--- a/src/views/object-storage/S3KeyView.vue
+++ b/src/views/object-storage/S3KeyView.vue
@@ -19,7 +19,7 @@ watch(
   () => props.s3key.access_key,
   () => {
     visibleSecret.value = false;
-  }
+  },
 );
 
 const visibleSecret = ref<boolean>(false);
diff --git a/src/views/object-storage/S3KeysView.vue b/src/views/object-storage/S3KeysView.vue
index 7e8ba0641874715cf89646df536ff3b89d78eb39..eaf3f920a6c8615e0340519cabd49597800c0937 100644
--- a/src/views/object-storage/S3KeysView.vue
+++ b/src/views/object-storage/S3KeysView.vue
@@ -50,7 +50,7 @@ function deleteKey(accessKey: string) {
         keyState.deletedKey = accessKey;
         keyState.activeKey = 0;
         keyState.keys = keyState.keys.filter(
-          (s3key) => s3key.access_key !== accessKey
+          (s3key) => s3key.access_key !== accessKey,
         );
         authStore.setS3Key(keyState.keys[0]);
         successToast?.show();
@@ -65,7 +65,7 @@ function createKey() {
       .then((s3key) => {
         keyState.keys.push(s3key);
         keyState.keys = [...keyState.keys].sort((keyA, keyB) =>
-          keyA.access_key > keyB.access_key ? 1 : -1
+          keyA.access_key > keyB.access_key ? 1 : -1,
         );
       })
       .catch((err) => console.error(err));
diff --git a/src/views/workflows/ArbitraryWorkflowView.vue b/src/views/workflows/ArbitraryWorkflowView.vue
index 6cbf6d1c2c42c641601ec6dd64cc911081d89b15..cc1c87b0c28732dd69e14989523e1f7c01230f64 100644
--- a/src/views/workflows/ArbitraryWorkflowView.vue
+++ b/src/views/workflows/ArbitraryWorkflowView.vue
@@ -11,6 +11,7 @@ import { Toast } from "bootstrap";
 const props = defineProps<{
   repository?: string;
   commit_hash?: string;
+  token?: string;
 }>();
 
 const router = useRouter();
@@ -37,12 +38,13 @@ const workflowExecutionState = reactive<{
 const showDocumentation = ref<boolean>(true);
 let errorToast: Toast | null = null;
 
-function downloadVersionFiles(repository: string, commit_hash: string) {
+function downloadVersionFiles(
+  repository: string,
+  commit_hash: string,
+  token?: string,
+) {
   workflowState.loading = true;
-  const repo = GitRepository.buildRepository(repository, commit_hash);
-  //const descriptionPromise = repo.downloadFile().then((response) => {
-  // versionState.descriptionMarkdown = response.data;
-  //});
+  const repo = GitRepository.buildRepository(repository, commit_hash, token);
   Promise.all(
     requiredRepositoryFiles.map((file) =>
       repo.downloadFile(file).then((response) => {
@@ -57,8 +59,8 @@ function downloadVersionFiles(repository: string, commit_hash: string) {
         } else if (file.includes("output")) {
           workflowState.outputMarkdown = response.data;
         }
-      })
-    )
+      }),
+    ),
   ).finally(() => {
     workflowState.loading = false;
   });
@@ -68,7 +70,7 @@ function startWorkflow(
   // eslint-disable-next-line @typescript-eslint/no-explicit-any
   parameters: Record<string, any>,
   notes?: string,
-  report_output_bucket?: string
+  report_output_bucket?: string,
 ) {
   if (props.repository && props.commit_hash) {
     errorToast?.hide();
@@ -78,6 +80,7 @@ function startWorkflow(
       parameters: parameters,
       report_output_bucket: report_output_bucket,
       repository_url: props.repository,
+      token: props.token,
     })
       .then(() => {
         router.push({
@@ -100,7 +103,7 @@ function startWorkflow(
 onMounted(() => {
   errorToast = new Toast("#arbitraryWorkflowExecutionViewErrorToast");
   if (props.commit_hash && props.repository) {
-    downloadVersionFiles(props.repository, props.commit_hash);
+    downloadVersionFiles(props.repository, props.commit_hash, props.token);
   }
 });
 </script>
diff --git a/src/views/workflows/ListWorkflowExecutionsView.vue b/src/views/workflows/ListWorkflowExecutionsView.vue
index b5068c2719efedd2cbbd2b4ee50b8dd9ae0cdab4..d40942a4e358ed57dda33a82d55f29b21ff761a4 100644
--- a/src/views/workflows/ListWorkflowExecutionsView.vue
+++ b/src/views/workflows/ListWorkflowExecutionsView.vue
@@ -52,7 +52,7 @@ const sortedExecutions = computed<WorkflowExecutionOut[]>(() => {
   const tempList = [...executionsState.workflowExecutions];
   tempList.sort((a, b) => {
     // sort by start time descending
-    return dayjs(a.start_time).isBefore(dayjs(b.start_time)) ? 1 : -1;
+    return dayjs.unix(a.start_time).isBefore(dayjs.unix(b.start_time)) ? 1 : -1;
   });
   return tempList;
 });
@@ -64,9 +64,9 @@ const deleteModalString = computed<string>(() => {
     !executionsState.executionToDelete.workflow_version_id ||
     !executionsState.executionToDelete.workflow_id
   ) {
-    return `Workflow Execution from ${dayjs(
-      executionsState.executionToDelete.start_time
-    ).format("DD.MM.YYYY HH:mm")}`;
+    return `Workflow Execution from ${dayjs
+      .unix(executionsState.executionToDelete.start_time)
+      .format("DD.MM.YYYY HH:mm")}`;
   } else {
     return `Workflow Execution ${
       executionsState.workflowMapping[
@@ -76,9 +76,9 @@ const deleteModalString = computed<string>(() => {
       executionsState.versionMapping[
         executionsState.executionToDelete.workflow_version_id
       ]
-    } from ${dayjs(executionsState.executionToDelete.start_time).format(
-      "DD.MM.YYYY HH:mm"
-    )}`;
+    } from ${dayjs
+      .unix(executionsState.executionToDelete.start_time)
+      .format("DD.MM.YYYY HH:mm")}`;
   }
 });
 
@@ -87,7 +87,7 @@ const deleteModalString = computed<string>(() => {
 function updateExecutions() {
   const listExecutionsPromise =
     WorkflowExecutionService.workflowExecutionListWorkflowExecutions(
-      userStore.currentUID
+      userStore.currentUID,
     )
       .then((executions) => {
         executionsState.workflowExecutions = executions;
@@ -104,24 +104,26 @@ function updateExecutions() {
           .filter((execution) => execution.workflow_id) // filter undefined workflows
           .filter(
             (execution) =>
-              !executionsState.workflowMapping[execution.workflow_id]
+              // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+              !executionsState.workflowMapping[execution.workflow_id!],
           )
           .filter(
             // filter unique workflows
             (execution, index, array) =>
               array.findIndex(
-                (val) => val.workflow_id === execution.workflow_id
-              ) === index
+                (val) => val.workflow_id === execution.workflow_id,
+              ) === index,
           )
           .map((execution) =>
-            WorkflowService.workflowGetWorkflow(execution.workflow_id)
-          )
-      )
+            // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+            WorkflowService.workflowGetWorkflow(execution.workflow_id!),
+          ),
+      ),
     )
     .then((workflows) =>
       workflows.forEach((workflow) => {
         executionsState.workflowMapping[workflow.workflow_id] = workflow.name;
-      })
+      }),
     )
     .finally(() => {
       executionsState.mappingLoading = false;
@@ -134,34 +136,37 @@ function updateExecutions() {
           .filter(
             // filter undefined workflow versions
             (execution) =>
-              execution.workflow_id && execution.workflow_version_id
+              execution.workflow_id && execution.workflow_version_id,
           )
           .filter(
             // filter already seen workflow versions
             (version) =>
-              !executionsState.versionMapping[version.workflow_version_id]
+              // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+              !executionsState.versionMapping[version.workflow_version_id!],
           )
           .filter(
             // filter unique workflow versions
             (execution, index, array) =>
               array.findIndex(
                 (val) =>
-                  val.workflow_version_id === execution.workflow_version_id
-              ) === index
+                  val.workflow_version_id === execution.workflow_version_id,
+              ) === index,
           )
           .map((execution) =>
             WorkflowVersionService.workflowVersionGetWorkflowVersion(
-              execution.workflow_version_id,
-              execution.workflow_id
-            )
-          )
-      )
+              // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+              execution.workflow_version_id!,
+              // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+              execution.workflow_id!,
+            ),
+          ),
+      ),
     )
     .then((versions) =>
       versions.forEach((version) => {
         executionsState.versionMapping[version.git_commit_hash] =
           version.version;
-      })
+      }),
     );
 }
 
@@ -172,7 +177,7 @@ function workflowExecutionDeletable(status: WorkflowExecutionStatus): boolean {
     WorkflowExecutionStatus.SUCCESS,
   ].includes(status);
 }
-function workflowExecutionCancable(status: WorkflowExecutionStatus): boolean {
+function workflowExecutionCancelable(status: WorkflowExecutionStatus): boolean {
   return [
     WorkflowExecutionStatus.RUNNING,
     WorkflowExecutionStatus.PENDING,
@@ -183,11 +188,11 @@ function workflowExecutionCancable(status: WorkflowExecutionStatus): boolean {
 function deleteWorkflowExecution(executionId?: string) {
   if (executionId) {
     WorkflowExecutionService.workflowExecutionDeleteWorkflowExecution(
-      executionId
+      executionId,
     ).then(() => {
       executionsState.workflowExecutions =
         executionsState.workflowExecutions.filter(
-          (execution) => execution.execution_id !== executionId
+          (execution) => execution.execution_id !== executionId,
         );
     });
   }
@@ -195,16 +200,15 @@ function deleteWorkflowExecution(executionId?: string) {
 
 function cancelWorkflowExecution(executionId: string) {
   WorkflowExecutionService.workflowExecutionCancelWorkflowExecution(
-    executionId
+    executionId,
   ).then(() => {
     const index = executionsState.workflowExecutions.findIndex(
-      (execution) => execution.execution_id === executionId
+      (execution) => execution.execution_id === executionId,
     );
     if (index > -1) {
       executionsState.workflowExecutions[index].status =
         WorkflowExecutionStatus.CANCELED;
-      executionsState.workflowExecutions[index].end_time =
-        dayjs().toISOString();
+      executionsState.workflowExecutions[index].end_time = dayjs().unix();
     }
   });
 }
@@ -300,7 +304,7 @@ onMounted(() => {
           </td>
           <td v-else>
             <router-link
-              v-if="execution.workflow_id"
+              v-if="execution.workflow_id && execution.workflow_version_id"
               :to="{
                 name: 'workflow-version',
                 params: {
@@ -324,10 +328,12 @@ onMounted(() => {
               />{{ execution.status }}</span
             >
           </td>
-          <td>{{ dayjs(execution.start_time).format("DD.MM.YYYY HH:mm") }}</td>
+          <td>
+            {{ dayjs.unix(execution.start_time).format("DD.MM.YYYY HH:mm") }}
+          </td>
           <td>
             <template v-if="execution.end_time">
-              {{ dayjs(execution.end_time).format("DD.MM.YYYY HH:mm") }}
+              {{ dayjs.unix(execution.end_time).format("DD.MM.YYYY HH:mm") }}
             </template>
             <template v-else> - </template>
           </td>
@@ -347,7 +353,7 @@ onMounted(() => {
                 <span class="visually-hidden">Toggle Dropdown</span>
               </button>
               <ul class="dropdown-menu dropdown-menu">
-                <li v-if="workflowExecutionCancable(execution.status)">
+                <li v-if="workflowExecutionCancelable(execution.status)">
                   <button
                     class="dropdown-item text-danger align-middle"
                     type="button"
diff --git a/src/views/workflows/ListWorkflowsView.vue b/src/views/workflows/ListWorkflowsView.vue
index 66d7f92a535e280ef044d2f9375841d63ffbbba9..2cbb2e0569173e5359991315414aa403be8fbc0a 100644
--- a/src/views/workflows/ListWorkflowsView.vue
+++ b/src/views/workflows/ListWorkflowsView.vue
@@ -30,8 +30,8 @@ const filterFunctionMapping: Record<
   name: (a: WorkflowOut, b: WorkflowOut) =>
     workflowsState.sortDesc ? a.name > b.name : a.name < b.name,
   release: (a: WorkflowOut, b: WorkflowOut) => {
-    const a_date = dayjs(a.versions[a.versions.length - 1].created_at);
-    const b_date = dayjs(b.versions[b.versions.length - 1].created_at);
+    const a_date = dayjs.unix(a.versions[a.versions.length - 1].created_at);
+    const b_date = dayjs.unix(b.versions[b.versions.length - 1].created_at);
     return workflowsState.sortDesc
       ? a_date.isBefore(b_date)
       : a_date.isAfter(b_date);
@@ -53,10 +53,10 @@ const processedWorkflows = computed<WorkflowOut[]>(() => {
     ...workflowsState.workflows.filter(
       (workflow) =>
         filterWorkflowByString(workflow) &&
-        filterWorkflowWithoutVersion(workflow)
+        filterWorkflowWithoutVersion(workflow),
     ),
   ].sort((a, b) =>
-    filterFunctionMapping[workflowsState.sortByAttribute](a, b) ? 1 : -1
+    filterFunctionMapping[workflowsState.sortByAttribute](a, b) ? 1 : -1,
   );
 });
 
@@ -204,6 +204,7 @@ onMounted(() => {
         workflow_id: '',
         versions: [],
         developer_id: '',
+        private: false,
       }"
       loading
     />
diff --git a/src/views/workflows/MyWorkflowsView.vue b/src/views/workflows/MyWorkflowsView.vue
index f9507273f1b97194524127a0d9edb96a43de8312..882df7869b1feb2b2c6338b46e4f8a0604d3a6de 100644
--- a/src/views/workflows/MyWorkflowsView.vue
+++ b/src/views/workflows/MyWorkflowsView.vue
@@ -1,6 +1,6 @@
 <script setup lang="ts">
 import { onMounted, reactive } from "vue";
-import type { WorkflowOut, WorkflowVersionFull } from "@/client/workflow";
+import type { WorkflowOut, WorkflowVersion } from "@/client/workflow";
 import { Status, WorkflowService } from "@/client/workflow";
 import { useAuthStore } from "@/stores/auth";
 import WorkflowWithVersionsCard from "@/components/workflows/WorkflowWithVersionsCard.vue";
@@ -8,6 +8,8 @@ import CreateWorkflowModal from "@/components/workflows/modals/CreateWorkflowMod
 import CardTransitionGroup from "@/components/transitions/CardTransitionGroup.vue";
 import UpdateWorkflowModal from "@/components/workflows/modals/UpdateWorkflowModal.vue";
 import DeleteModal from "@/components/modals/DeleteModal.vue";
+import UpdateWorkflowVersionIconModal from "@/components/workflows/modals/UpdateWorkflowVersionIconModal.vue";
+import UpdateWorkflowCredentialsModal from "@/components/workflows/modals/UpdateWorkflowCredentialsModal.vue";
 
 const userRepository = useAuthStore();
 const workflowsState = reactive<{
@@ -15,6 +17,7 @@ const workflowsState = reactive<{
   loading: boolean;
   updateWorkflow: WorkflowOut;
   potentialWorkflowDelete?: WorkflowOut;
+  updateIconVersion: WorkflowVersion;
 }>({
   workflows: [],
   loading: true,
@@ -25,14 +28,27 @@ const workflowsState = reactive<{
     versions: [
       {
         version: "1.0.0",
-        created_at: "01.01.2023",
+        created_at: 0,
         git_commit_hash: "",
         status: Status.CREATED,
+        workflow_id: "",
+        icon_url: "",
+        modes: [],
       },
     ],
     repository_url: "",
     workflow_id: "",
     developer_id: "",
+    private: false,
+  },
+  updateIconVersion: {
+    version: "",
+    workflow_id: "",
+    git_commit_hash: "",
+    modes: null,
+    icon_url: null,
+    created_at: 0,
+    status: Status.CREATED,
   },
 });
 
@@ -40,16 +56,14 @@ function workflowUpdateClicked(workflow: WorkflowOut) {
   workflowsState.updateWorkflow = workflow;
 }
 
-function workflowUpdated(version: WorkflowVersionFull) {
+function iconUpdateClicked(version: WorkflowVersion) {
+  workflowsState.updateIconVersion = version;
+}
+
+function workflowUpdated(version: WorkflowVersion) {
   workflowsState.workflows
     .find((w) => w.workflow_id == version.workflow_id)
-    ?.versions.push({
-      status: version.status,
-      git_commit_hash: version.git_commit_hash,
-      icon_url: version.icon_url,
-      created_at: version.created_at,
-      version: version.version,
-    });
+    ?.versions.push(version);
 }
 
 function workflowDeleteClicked(workflow: WorkflowOut) {
@@ -61,7 +75,7 @@ function confirmedWorkflowDelete(workflow_id?: string) {
     WorkflowService.workflowDeleteWorkflow(workflow_id)
       .then(() => {
         workflowsState.workflows = workflowsState.workflows.filter(
-          (workflow) => workflow.workflow_id !== workflow_id
+          (workflow) => workflow.workflow_id !== workflow_id,
         );
       })
       .finally(() => {
@@ -70,11 +84,47 @@ function confirmedWorkflowDelete(workflow_id?: string) {
   }
 }
 
+function workflowCredentialsUpdated(
+  workflow: WorkflowOut,
+  credentialsDeleted: boolean,
+) {
+  const index = workflowsState.workflows.findIndex(
+    (w) => w.workflow_id == workflow.workflow_id,
+  );
+  if (index > -1) {
+    workflowsState.workflows[index].private = !credentialsDeleted;
+  }
+}
+
+function confirmedWorkflowVersionIconUpdate(
+  version: WorkflowVersion,
+  icon_url?: string,
+) {
+  const wIndex = workflowsState.workflows.findIndex(
+    (w) => w.workflow_id == version.workflow_id,
+  );
+  if (wIndex > -1) {
+    const vIndex = workflowsState.workflows[wIndex].versions.findIndex(
+      (v) => v.git_commit_hash == version.git_commit_hash,
+    );
+    if (vIndex > -1) {
+      setTimeout(() => {
+        workflowsState.workflows[wIndex].versions[vIndex].icon_url =
+          icon_url ?? null;
+      }, 1000);
+    }
+  }
+}
+
+function updateIconClicked(version: WorkflowVersion) {
+  workflowsState.updateIconVersion = version;
+}
+
 onMounted(() => {
   WorkflowService.workflowListWorkflows(
     undefined,
     Object.values(Status),
-    userRepository.currentUID
+    userRepository.currentUID,
   )
     .then((workflows) => {
       workflowsState.workflows = workflows;
@@ -100,10 +150,26 @@ onMounted(() => {
     :object-name-delete="workflowsState.potentialWorkflowDelete?.name"
     @confirm-delete="
       confirmedWorkflowDelete(
-        workflowsState.potentialWorkflowDelete?.workflow_id
+        workflowsState.potentialWorkflowDelete?.workflow_id,
       )
     "
   />
+  <update-workflow-version-icon-modal
+    modal-i-d="updateWorkflowVersionIconModal"
+    :workflow-name="
+      workflowsState.workflows.find(
+        (w) => w.workflow_id == workflowsState.updateIconVersion.workflow_id,
+      )?.name
+    "
+    :version="workflowsState.updateIconVersion"
+    @icon-updated="confirmedWorkflowVersionIconUpdate"
+    @icon-deleted="confirmedWorkflowVersionIconUpdate"
+  />
+  <update-workflow-credentials-modal
+    :workflow="workflowsState.updateWorkflow"
+    modal-i-d="updateWorkflowCredentialsModal"
+    @credentials-updated="workflowCredentialsUpdated"
+  />
   <div
     class="row m-2 border-bottom mb-4 justify-content-between align-items-center pb-2"
   >
@@ -127,7 +193,9 @@ onMounted(() => {
         :workflow="workflow"
         :loading="false"
         @workflow-update-click="workflowUpdateClicked"
+        @workflow-update-credentials-click="workflowUpdateClicked"
         @workflow-delete-click="workflowDeleteClicked"
+        @workflow-update-icon-click="iconUpdateClicked"
       />
     </card-transition-group>
     <div v-else class="text-center mt-5 fs-2">
@@ -151,8 +219,10 @@ onMounted(() => {
         repository_url: '',
         short_description: '',
         developer_id: '',
+        private: false,
       }"
       loading
+      @workflow-update-icon-click="updateIconClicked"
     />
   </div>
 </template>
diff --git a/src/views/workflows/ReviewWorkflowsView.vue b/src/views/workflows/ReviewWorkflowsView.vue
index c752ad89036e6648a8fe436148ad9e65dd8144c3..fd121ea16ac775d9c0c96742781fe7cac805bf46 100644
--- a/src/views/workflows/ReviewWorkflowsView.vue
+++ b/src/views/workflows/ReviewWorkflowsView.vue
@@ -28,23 +28,23 @@ const workflowsState = reactive<{
 function updateWorkflowVersionStatus(
   workflow_id: string,
   git_commit_hash: string,
-  new_status: Status
+  new_status: Status,
 ) {
   workflowsState.versionUpdateLoading = git_commit_hash;
   WorkflowVersionService.workflowVersionUpdateWorkflowVersionStatus(
     git_commit_hash,
     workflow_id,
-    { status: new_status } as WorkflowVersionStatus
+    { status: new_status } as WorkflowVersionStatus,
   )
     .then(() => {
       const workflowIndex = workflowsState.workflows.findIndex(
-        (workflow) => workflow.workflow_id === workflow_id
+        (workflow) => workflow.workflow_id === workflow_id,
       );
       if (workflowIndex >= 0) {
         const versionIndex = workflowsState.workflows[
           workflowIndex
         ].versions.findIndex(
-          (version) => version.git_commit_hash === git_commit_hash
+          (version) => version.git_commit_hash === git_commit_hash,
         );
         if (versionIndex >= 0) {
           workflowsState.workflows[workflowIndex].versions[
@@ -64,8 +64,8 @@ function fetchDevelopers(workflows: WorkflowOut[]) {
     workflows.map((workflow) =>
       UserService.userGetUser(workflow.developer_id).then((developer) => {
         workflowsState.developers[developer.uid] = developer.display_name;
-      })
-    )
+      }),
+    ),
   ).finally(() => {
     workflowsState.developersLoading = false;
   });
@@ -210,7 +210,7 @@ onMounted(() => {
                           updateWorkflowVersionStatus(
                             workflow.workflow_id,
                             version.git_commit_hash,
-                            Status.PUBLISHED
+                            Status.PUBLISHED,
                           )
                         "
                       >
@@ -223,7 +223,7 @@ onMounted(() => {
                           updateWorkflowVersionStatus(
                             workflow.workflow_id,
                             version.git_commit_hash,
-                            Status.DENIED
+                            Status.DENIED,
                           )
                         "
                       >
diff --git a/src/views/workflows/StartWorkflowView.vue b/src/views/workflows/StartWorkflowView.vue
index f0f9d1f2d1cea3dc245e4ce81c1556e6d8200772..6a6dba2545d4b2bbb27f325e2c5d124932f2ec46 100644
--- a/src/views/workflows/StartWorkflowView.vue
+++ b/src/views/workflows/StartWorkflowView.vue
@@ -1,12 +1,12 @@
 <script setup lang="ts">
 import ParameterSchemaFormComponent from "@/components/parameter-schema/ParameterSchemaFormComponent.vue";
-import type { WorkflowVersionFull } from "@/client/workflow";
+import type { WorkflowVersion } from "@/client/workflow";
 import {
   ApiError,
+  DocumentationEnum,
   WorkflowExecutionService,
   WorkflowVersionService,
 } from "@/client/workflow";
-import axios from "axios";
 import { onMounted, ref, reactive } from "vue";
 import { useRouter } from "vue-router";
 import { Toast } from "bootstrap";
@@ -23,7 +23,7 @@ let errorToast: Toast | null = null;
 const versionState = reactive<{
   // eslint-disable-next-line @typescript-eslint/no-explicit-any
   parameterSchema?: Record<string, any>;
-  workflowVersion?: WorkflowVersionFull;
+  workflowVersion?: WorkflowVersion;
   loading: boolean;
   workflowExecutionError?: string;
 }>({
@@ -36,7 +36,7 @@ const versionState = reactive<{
 function downloadVersion() {
   WorkflowVersionService.workflowVersionGetWorkflowVersion(
     props.versionId,
-    props.workflowId
+    props.workflowId,
   )
     .then((version) => {
       versionState.workflowVersion = version;
@@ -45,9 +45,13 @@ function downloadVersion() {
     .then(downloadVersionFiles);
 }
 
-function downloadVersionFiles(version: WorkflowVersionFull) {
-  axios.get(version.parameter_schema_url).then((response) => {
-    parameterSchema.value = response.data;
+function downloadVersionFiles(version: WorkflowVersion) {
+  WorkflowVersionService.workflowVersionDownloadWorkflowDocumentation(
+    version.workflow_id,
+    version.git_commit_hash,
+    DocumentationEnum.PARAMETER_SCHEMA,
+  ).then((markdown) => {
+    parameterSchema.value = markdown;
   });
 }
 
@@ -55,7 +59,7 @@ function startWorkflow(
   // eslint-disable-next-line @typescript-eslint/no-explicit-any
   parameters: Record<string, any>,
   notes?: string,
-  report_output_bucket?: string
+  report_output_bucket?: string,
 ) {
   if (props.versionId) {
     versionState.workflowExecutionError = undefined;
diff --git a/src/views/workflows/WorkflowVersionView.vue b/src/views/workflows/WorkflowVersionView.vue
index 4b070c2e1fd65ff9952268f0d36076e5624c9d3b..42de6e1fddad6a8e27da3cfbb69136abbc90b946 100644
--- a/src/views/workflows/WorkflowVersionView.vue
+++ b/src/views/workflows/WorkflowVersionView.vue
@@ -1,8 +1,7 @@
 <script setup lang="ts">
 import { onMounted, reactive, watch } from "vue";
-import { WorkflowVersionService } from "@/client/workflow";
-import type { WorkflowVersionFull } from "@/client/workflow";
-import axios from "axios";
+import type { WorkflowVersion } from "@/client/workflow";
+import { DocumentationEnum, WorkflowVersionService } from "@/client/workflow";
 import WorkflowDocumentationTabs from "@/components/workflows/WorkflowDocumentationTabs.vue";
 
 const props = defineProps<{
@@ -13,7 +12,7 @@ const props = defineProps<{
 const versionState = reactive<{
   loading: boolean;
   fileLoading: boolean;
-  version: undefined | WorkflowVersionFull;
+  version: undefined | WorkflowVersion;
   descriptionMarkdown: string;
   changelogMarkdown: string;
   usageMarkdown: string;
@@ -39,7 +38,7 @@ watch(
       // If bucket is changed, update the objects
       updateVersion(newVersionId, props.workflowId);
     }
-  }
+  },
 );
 
 function updateVersion(versionId: string, workflowId: string) {
@@ -47,7 +46,7 @@ function updateVersion(versionId: string, workflowId: string) {
   versionState.fileLoading = true;
   WorkflowVersionService.workflowVersionGetWorkflowVersion(
     versionId,
-    workflowId
+    workflowId,
   )
     .then((version) => {
       versionState.version = version;
@@ -62,25 +61,48 @@ function updateVersion(versionId: string, workflowId: string) {
     });
 }
 
-function downloadVersionFiles(version: WorkflowVersionFull) {
+function downloadVersionFiles(version: WorkflowVersion) {
   versionState.fileLoading = true;
-  const descriptionPromise = axios.get(version.readme_url).then((response) => {
-    versionState.descriptionMarkdown = response.data;
-  });
-  const changelogPromise = axios.get(version.changelog_url).then((response) => {
-    versionState.changelogMarkdown = response.data;
-  });
-  const parameterPromise = axios
-    .get(version.parameter_schema_url)
-    .then((response) => {
-      versionState.parameterSchema = response.data;
+  const descriptionPromise =
+    WorkflowVersionService.workflowVersionDownloadWorkflowDocumentation(
+      version.workflow_id,
+      version.git_commit_hash,
+      DocumentationEnum.USAGE,
+    ).then((markdown) => {
+      versionState.descriptionMarkdown = markdown;
+    });
+  const changelogPromise =
+    WorkflowVersionService.workflowVersionDownloadWorkflowDocumentation(
+      version.workflow_id,
+      version.git_commit_hash,
+      DocumentationEnum.CHANGELOG,
+    ).then((markdown) => {
+      versionState.changelogMarkdown = markdown;
+    });
+  const parameterPromise =
+    WorkflowVersionService.workflowVersionDownloadWorkflowDocumentation(
+      version.workflow_id,
+      version.git_commit_hash,
+      DocumentationEnum.PARAMETER_SCHEMA,
+    ).then((markdown) => {
+      versionState.parameterSchema = markdown;
+    });
+  const usagePromise =
+    WorkflowVersionService.workflowVersionDownloadWorkflowDocumentation(
+      version.workflow_id,
+      version.git_commit_hash,
+      DocumentationEnum.INPUT,
+    ).then((markdown) => {
+      versionState.usageMarkdown = markdown;
+    });
+  const outputPromise =
+    WorkflowVersionService.workflowVersionDownloadWorkflowDocumentation(
+      version.workflow_id,
+      version.git_commit_hash,
+      DocumentationEnum.OUTPUT,
+    ).then((markdown) => {
+      versionState.outputMarkdown = markdown;
     });
-  const usagePromise = axios.get(version.usage_url).then((response) => {
-    versionState.usageMarkdown = response.data;
-  });
-  const outputPromise = axios.get(version.output_url).then((response) => {
-    versionState.outputMarkdown = response.data;
-  });
   Promise.all([
     descriptionPromise,
     changelogPromise,
diff --git a/src/views/workflows/WorkflowView.vue b/src/views/workflows/WorkflowView.vue
index e623dcfaf2295ce0d18a37b6633a8c05733cf51c..05e3ee2cfe6507f6fc40e63604ab74e8071ba375 100644
--- a/src/views/workflows/WorkflowView.vue
+++ b/src/views/workflows/WorkflowView.vue
@@ -3,7 +3,7 @@ import { computed, onMounted, reactive, watch } from "vue";
 import type {
   WorkflowOut,
   WorkflowStatistic,
-  WorkflowVersionReduced,
+  WorkflowVersion,
 } from "@/client/workflow";
 import WorkflowStatisticsChart from "@/components/workflows/WorkflowStatisticsChart.vue";
 import {
@@ -58,14 +58,14 @@ watch(
     if (newWorkflowId !== oldWorkflowId) {
       updateWorkflow(newWorkflowId);
     }
-  }
+  },
 );
 
 watch(
   () => props.versionId,
   (newWorkflowId) => {
     workflowState.activeVersionId = newWorkflowId ?? "";
-  }
+  },
 );
 
 watch(
@@ -82,38 +82,39 @@ watch(
         query: { tab: route.query.tab },
       });
     }
-  }
+  },
 );
 
 // Computed Properties
 // =============================================================================
-const latestVersion = computed<WorkflowVersionReduced | undefined>(() =>
+const latestVersion = computed<WorkflowVersion | undefined>(() =>
   calculateLatestVersion(
     workflowState.workflow?.versions?.filter(
-      (version) => version.status == Status.PUBLISHED
-    ) || []
-  )
+      (version) => version.status == Status.PUBLISHED,
+    ) || [],
+  ),
 );
-const activeVersion = computed<WorkflowVersionReduced | undefined>(() =>
-  workflowState.workflow?.versions.find(
-    (w) => w.git_commit_hash === workflowState.activeVersionId
-  )
+const activeVersion = computed<WorkflowVersion | undefined>(
+  () =>
+    workflowState.workflow?.versions.find(
+      (w) => w.git_commit_hash === workflowState.activeVersionId,
+    ),
 );
 
 const activeVersionString = computed<string>(
-  () => activeVersion.value?.version ?? ""
+  () => activeVersion.value?.version ?? "",
 );
 
 const activeVersionIcon = computed<string | undefined>(
-  () => activeVersion.value?.icon_url
+  () => activeVersion.value?.icon_url ?? undefined,
 );
 
 const versionLaunchable = computed<boolean>(
-  () => activeVersion.value?.status == Status.PUBLISHED ?? false
+  () => activeVersion.value?.status == Status.PUBLISHED ?? false,
 );
 
 const gitIcon = computed<string>(() =>
-  determineGitIcon(workflowState.workflow?.repository_url)
+  determineGitIcon(workflowState.workflow?.repository_url),
 );
 
 const allowVersionDeprecation = computed<boolean>(() => {
@@ -160,11 +161,11 @@ function deprecateCurrentWorkflowVersion() {
   if (props.versionId) {
     WorkflowVersionService.workflowVersionDeprecateWorkflowVersion(
       props.versionId,
-      props.workflowId
+      props.workflowId,
     ).then((version) => {
       if (workflowState.workflow) {
         const versionIndex = workflowState.workflow.versions.findIndex(
-          (v) => v.git_commit_hash === version.git_commit_hash
+          (v) => v.git_commit_hash === version.git_commit_hash,
         );
         if (versionIndex > -1) {
           workflowState.workflow.versions[versionIndex].status = version.status;
@@ -206,13 +207,12 @@ onMounted(() => {
         {{ workflowState.workflow.name }}
         <span v-if="activeVersionString">@{{ activeVersionString }}</span>
       </div>
-      <a :href="workflowState.workflow.repository_url" target="_blank">
-        <img
-          v-if="activeVersionIcon != null"
-          :src="activeVersionIcon"
-          class="img-fluid icon"
-          alt="Workflow icon"
-      /></a>
+      <img
+        v-if="activeVersionIcon != null"
+        :src="activeVersionIcon"
+        class="img-fluid icon"
+        alt="Workflow icon"
+      />
     </div>
     <p class="fs-4 mb-5 mt-3">{{ workflowState.workflow.short_description }}</p>
     <template v-if="route.name !== 'workflow-start'">
@@ -232,8 +232,8 @@ onMounted(() => {
             },
             query: { tab: route.query.tab },
           }"
-          >Try the latest version {{ latestVersion.version }}.</router-link
-        >
+          >Try the latest version {{ latestVersion.version }}.
+        </router-link>
       </div>
       <div class="row align-items-center">
         <div class="w-fit position-absolute start-0">
@@ -293,8 +293,13 @@ onMounted(() => {
           <font-awesome-icon :icon="gitIcon" class="me-1" />
           <span class="align-middle">
             {{ workflowState.workflow.repository_url }}</span
-          ></a
-        >
+          >
+          <font-awesome-icon
+            v-if="workflowState.workflow?.private"
+            icon="fa-solid fa-lock"
+            class="ms-1"
+          />
+        </a>
       </div>
       <workflow-statistics-chart
         :stats="workflowState.stats"
@@ -317,13 +322,9 @@ onMounted(() => {
 </template>
 
 <style scoped>
-.icon:hover {
-  opacity: 0.8;
-}
-
 .icon {
-  max-width: 60px;
-  max-height: 60px;
+  max-width: 64px;
+  max-height: 64px;
   min-width: 50px;
   min-height: 50px;
 }
diff --git a/tsconfig.json b/tsconfig.json
index 0f216f1416aff7a546a0218b93976f2282c8460f..32d333fdaaf1d55c8b026efa8c95a87163d171f1 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -1,18 +1,16 @@
 {
-  "extends": "@vue/tsconfig/tsconfig.web.json",
+  "extends": [
+    "@tsconfig/node18/tsconfig.json",
+    "@vue/tsconfig/tsconfig.json"
+  ],
   "include": ["env.d.ts", "src/**/*", "src/**/*.vue"],
   "compilerOptions": {
     "lib": ["...", "dom"],
     "allowSyntheticDefaultImports": true,
     "baseUrl": ".",
+    "types": ["node"],
     "paths": {
       "@/*": ["./src/*"]
     }
   },
-
-  "references": [
-    {
-      "path": "./tsconfig.config.json"
-    }
-  ]
 }