From ef04796dd04397e0ecea4d9664471d5d630d716e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Daniel=20G=C3=B6bel?= <dgoebel@techfak.uni-bielefeld.de>
Date: Wed, 10 Jan 2024 17:50:52 +0100
Subject: [PATCH] Add create resource modal

#88
---
 src/client/auth/core/CancelablePromise.ts     |   6 +-
 src/client/auth/core/request.ts               |  11 +-
 src/client/auth/models/ErrorDetail.ts         |   1 -
 src/client/auth/models/HTTPValidationError.ts |   2 -
 src/client/auth/models/RoleEnum.ts            |   2 +-
 src/client/auth/models/User.ts                |   2 -
 src/client/auth/models/ValidationError.ts     |   1 -
 src/client/auth/services/AuthService.ts       |   4 -
 src/client/auth/services/UserService.ts       |  30 ++-
 src/client/resource/core/CancelablePromise.ts |   6 +-
 src/client/resource/core/request.ts           |  11 +-
 src/client/resource/models/ErrorDetail.ts     |   1 -
 .../resource/models/HTTPValidationError.ts    |   2 -
 src/client/resource/models/ResourceIn.ts      |   1 -
 src/client/resource/models/ResourceOut.ts     |   2 -
 .../resource/models/ResourceVersionIn.ts      |   1 -
 .../resource/models/ResourceVersionOut.ts     |   2 -
 src/client/resource/models/Status.ts          |   1 -
 src/client/resource/models/ValidationError.ts |   1 -
 .../resource/services/ResourceService.ts      |  17 +-
 .../services/ResourceVersionService.ts        |  13 +-
 src/client/s3proxy/core/CancelablePromise.ts  |   6 +-
 src/client/s3proxy/core/request.ts            |  11 +-
 src/client/s3proxy/models/BucketIn.ts         |   1 -
 src/client/s3proxy/models/BucketOut.ts        |   2 -
 .../s3proxy/models/BucketPermissionIn.ts      |   2 -
 .../s3proxy/models/BucketPermissionOut.ts     |   2 -
 .../models/BucketPermissionParameters.ts      |   2 -
 src/client/s3proxy/models/BucketType.ts       |   1 -
 src/client/s3proxy/models/Constraint.ts       |   1 -
 src/client/s3proxy/models/ErrorDetail.ts      |   1 -
 .../s3proxy/models/HTTPValidationError.ts     |   2 -
 src/client/s3proxy/models/Permission.ts       |   1 -
 src/client/s3proxy/models/PermissionStatus.ts |   1 -
 src/client/s3proxy/models/S3Key.ts            |   1 -
 src/client/s3proxy/models/ValidationError.ts  |   1 -
 .../services/BucketPermissionService.ts       |   9 -
 src/client/s3proxy/services/BucketService.ts  |   7 -
 src/client/s3proxy/services/S3KeyService.ts   |   7 -
 src/client/workflow/core/CancelablePromise.ts |   6 +-
 src/client/workflow/core/request.ts           |  11 +-
 .../models/AnonymizedWorkflowExecution.ts     |   2 -
 ...ow_Version_upload_workflow_version_icon.ts |   1 -
 .../workflow/models/DevWorkflowExecutionIn.ts |   2 -
 .../workflow/models/DocumentationEnum.ts      |   1 -
 src/client/workflow/models/ErrorDetail.ts     |   1 -
 .../workflow/models/HTTPValidationError.ts    |   2 -
 src/client/workflow/models/IconUpdateOut.ts   |   1 -
 src/client/workflow/models/Status.ts          |   1 -
 src/client/workflow/models/ValidationError.ts |   1 -
 .../workflow/models/WorkflowCredentialsIn.ts  |   1 -
 .../workflow/models/WorkflowCredentialsOut.ts |   1 -
 .../workflow/models/WorkflowExecutionIn.ts    |   1 -
 .../workflow/models/WorkflowExecutionOut.ts   |   2 -
 .../models/WorkflowExecutionStatus.ts         |   1 -
 src/client/workflow/models/WorkflowIn.ts      |   2 -
 src/client/workflow/models/WorkflowModeIn.ts  |   1 -
 src/client/workflow/models/WorkflowModeOut.ts |   1 -
 src/client/workflow/models/WorkflowOut.ts     |   2 -
 .../workflow/models/WorkflowStatistic.ts      |   1 -
 src/client/workflow/models/WorkflowUpdate.ts  |   2 -
 src/client/workflow/models/WorkflowVersion.ts |   2 -
 .../workflow/models/WorkflowVersionStatus.ts  |   2 -
 .../services/WorkflowCredentialsService.ts    |   6 -
 .../services/WorkflowExecutionService.ts      |  10 -
 .../workflow/services/WorkflowModeService.ts  |   4 -
 .../workflow/services/WorkflowService.ts      |  17 --
 .../services/WorkflowVersionService.ts        |  10 -
 src/components/NavbarTop.vue                  |  39 +++-
 .../ResourceCard.vue                          |   2 +-
 .../resources/createResourceModal.vue         | 205 ++++++++++++++++++
 src/router/index.ts                           | 107 +--------
 src/router/resourceRoutes.ts                  |  14 ++
 src/router/s3Routes.ts                        |  25 +++
 src/router/workflowRoutes.ts                  |  76 +++++++
 src/stores/resources.ts                       |  17 +-
 src/stores/users.ts                           |   6 +
 src/views/resources/ListResourcesView.vue     |   2 +-
 src/views/resources/MyResourcesView.vue       |  50 +++++
 79 files changed, 491 insertions(+), 322 deletions(-)
 rename src/components/{transitions => resources}/ResourceCard.vue (97%)
 create mode 100644 src/components/resources/createResourceModal.vue
 create mode 100644 src/router/resourceRoutes.ts
 create mode 100644 src/router/s3Routes.ts
 create mode 100644 src/router/workflowRoutes.ts
 create mode 100644 src/views/resources/MyResourcesView.vue

diff --git a/src/client/auth/core/CancelablePromise.ts b/src/client/auth/core/CancelablePromise.ts
index 55fef85..eb02246 100644
--- a/src/client/auth/core/CancelablePromise.ts
+++ b/src/client/auth/core/CancelablePromise.ts
@@ -51,7 +51,7 @@ export class CancelablePromise<T> implements Promise<T> {
                     return;
                 }
                 this.#isResolved = true;
-                this.#resolve?.(value);
+                if (this.#resolve) this.#resolve(value);
             };
 
             const onReject = (reason?: any): void => {
@@ -59,7 +59,7 @@ export class CancelablePromise<T> implements Promise<T> {
                     return;
                 }
                 this.#isRejected = true;
-                this.#reject?.(reason);
+                if (this.#reject) this.#reject(reason);
             };
 
             const onCancel = (cancelHandler: () => void): void => {
@@ -122,7 +122,7 @@ export class CancelablePromise<T> implements Promise<T> {
             }
         }
         this.#cancelHandlers.length = 0;
-        this.#reject?.(new CancelError('Request aborted'));
+        if (this.#reject) this.#reject(new CancelError('Request aborted'));
     }
 
     public get isCancelled(): boolean {
diff --git a/src/client/auth/core/request.ts b/src/client/auth/core/request.ts
index 1142d43..c6a0602 100644
--- a/src/client/auth/core/request.ts
+++ b/src/client/auth/core/request.ts
@@ -145,10 +145,13 @@ export const resolve = async <T>(options: ApiRequestOptions, resolver?: T | Reso
 };
 
 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);
-    const additionalHeaders = await resolve(options, config.HEADERS);
+    const [token, username, password, additionalHeaders] = await Promise.all([
+        resolve(options, config.TOKEN),
+        resolve(options, config.USERNAME),
+        resolve(options, config.PASSWORD),
+        resolve(options, config.HEADERS),
+    ]);
+
     const formHeaders = typeof formData?.getHeaders === 'function' && formData?.getHeaders() || {}
 
     const headers = Object.entries({
diff --git a/src/client/auth/models/ErrorDetail.ts b/src/client/auth/models/ErrorDetail.ts
index eba50ab..69dd37e 100644
--- a/src/client/auth/models/ErrorDetail.ts
+++ b/src/client/auth/models/ErrorDetail.ts
@@ -2,7 +2,6 @@
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
-
 /**
  * Schema for a error due to a rejected request.
  */
diff --git a/src/client/auth/models/HTTPValidationError.ts b/src/client/auth/models/HTTPValidationError.ts
index c0bcc87..892e425 100644
--- a/src/client/auth/models/HTTPValidationError.ts
+++ b/src/client/auth/models/HTTPValidationError.ts
@@ -2,9 +2,7 @@
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
-
 import type { ValidationError } from './ValidationError';
-
 export type HTTPValidationError = {
     detail?: Array<ValidationError>;
 };
diff --git a/src/client/auth/models/RoleEnum.ts b/src/client/auth/models/RoleEnum.ts
index c38121a..d1a23ae 100644
--- a/src/client/auth/models/RoleEnum.ts
+++ b/src/client/auth/models/RoleEnum.ts
@@ -2,7 +2,6 @@
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
-
 /**
  * Enumeration for the Roles in the CloWM Services.
  */
@@ -12,4 +11,5 @@ export enum RoleEnum {
     REVIEWER = 'reviewer',
     DEVELOPER = 'developer',
     FOREIGN_USER = 'foreign_user',
+    DB_MAINTAINER = 'db_maintainer',
 }
diff --git a/src/client/auth/models/User.ts b/src/client/auth/models/User.ts
index 8804790..5a1a2a0 100644
--- a/src/client/auth/models/User.ts
+++ b/src/client/auth/models/User.ts
@@ -2,9 +2,7 @@
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
-
 import type { RoleEnum } from './RoleEnum';
-
 /**
  * Schema for a user.
  */
diff --git a/src/client/auth/models/ValidationError.ts b/src/client/auth/models/ValidationError.ts
index 18997ec..f2ff49a 100644
--- a/src/client/auth/models/ValidationError.ts
+++ b/src/client/auth/models/ValidationError.ts
@@ -2,7 +2,6 @@
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
-
 export type ValidationError = {
     loc: Array<(string | number)>;
     msg: string;
diff --git a/src/client/auth/services/AuthService.ts b/src/client/auth/services/AuthService.ts
index 0472d39..ff4c021 100644
--- a/src/client/auth/services/AuthService.ts
+++ b/src/client/auth/services/AuthService.ts
@@ -5,9 +5,7 @@
 import type { CancelablePromise } from '../core/CancelablePromise';
 import { OpenAPI } from '../core/OpenAPI';
 import { request as __request } from '../core/request';
-
 export class AuthService {
-
     /**
      * Redirect to LifeScience OIDC Login
      * Redirect route to OIDC provider to kickstart the login process.
@@ -23,7 +21,6 @@ export class AuthService {
             },
         });
     }
-
     /**
      * LifeScience Login Callback
      * Callback for the Life Science Identity Provider.
@@ -48,5 +45,4 @@ export class AuthService {
             },
         });
     }
-
 }
diff --git a/src/client/auth/services/UserService.ts b/src/client/auth/services/UserService.ts
index ae57842..0e52ed2 100644
--- a/src/client/auth/services/UserService.ts
+++ b/src/client/auth/services/UserService.ts
@@ -4,17 +4,14 @@
 /* eslint-disable */
 import type { RoleEnum } from '../models/RoleEnum';
 import type { User } from '../models/User';
-
 import type { CancelablePromise } from '../core/CancelablePromise';
 import { OpenAPI } from '../core/OpenAPI';
 import { request as __request } from '../core/request';
-
 export class UserService {
-
     /**
      * Get the logged in user
      * Return the user associated with the used JWT.
-     * Permission 'user:read' required.
+     * Permission `user:read` required.
      * @returns User Successful Response
      * @throws ApiError
      */
@@ -29,21 +26,21 @@ export class UserService {
             },
         });
     }
-
     /**
      * List users and search by their name
      * 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' 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.
+     *
+     * 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 `user:search` required
+     * @param filterRoles Filter users by their role. If multiple are selected, they are concatenating by an OR Expression. Permission `user:read_any` required
+     * @param includeRoles Flag whether to include the roles of the users in the response. If True, permission `user:read_any` required.
      * @returns User Successful Response
      * @throws ApiError
      */
     public static userListUsers(
-        nameSubstring?: (string | null),
-        filterRoles?: (Array<RoleEnum> | null),
+        nameSubstring?: string,
+        filterRoles?: Array<RoleEnum>,
         includeRoles: boolean = false,
     ): CancelablePromise<Array<User>> {
         return __request(OpenAPI, {
@@ -62,13 +59,13 @@ 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
+     * Return the user with the specific uid.
+     *
+     * Permission `user:read` required if the current user has the same uid as `uid` otherwise `user:read_any` 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.
+     * @param includeRoles Flag whether to include the roles of the users in the response. If True, permission `user:read_any` required.
      * @returns User Successful Response
      * @throws ApiError
      */
@@ -93,5 +90,4 @@ export class UserService {
             },
         });
     }
-
 }
diff --git a/src/client/resource/core/CancelablePromise.ts b/src/client/resource/core/CancelablePromise.ts
index 55fef85..eb02246 100644
--- a/src/client/resource/core/CancelablePromise.ts
+++ b/src/client/resource/core/CancelablePromise.ts
@@ -51,7 +51,7 @@ export class CancelablePromise<T> implements Promise<T> {
                     return;
                 }
                 this.#isResolved = true;
-                this.#resolve?.(value);
+                if (this.#resolve) this.#resolve(value);
             };
 
             const onReject = (reason?: any): void => {
@@ -59,7 +59,7 @@ export class CancelablePromise<T> implements Promise<T> {
                     return;
                 }
                 this.#isRejected = true;
-                this.#reject?.(reason);
+                if (this.#reject) this.#reject(reason);
             };
 
             const onCancel = (cancelHandler: () => void): void => {
@@ -122,7 +122,7 @@ export class CancelablePromise<T> implements Promise<T> {
             }
         }
         this.#cancelHandlers.length = 0;
-        this.#reject?.(new CancelError('Request aborted'));
+        if (this.#reject) this.#reject(new CancelError('Request aborted'));
     }
 
     public get isCancelled(): boolean {
diff --git a/src/client/resource/core/request.ts b/src/client/resource/core/request.ts
index 1142d43..c6a0602 100644
--- a/src/client/resource/core/request.ts
+++ b/src/client/resource/core/request.ts
@@ -145,10 +145,13 @@ export const resolve = async <T>(options: ApiRequestOptions, resolver?: T | Reso
 };
 
 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);
-    const additionalHeaders = await resolve(options, config.HEADERS);
+    const [token, username, password, additionalHeaders] = await Promise.all([
+        resolve(options, config.TOKEN),
+        resolve(options, config.USERNAME),
+        resolve(options, config.PASSWORD),
+        resolve(options, config.HEADERS),
+    ]);
+
     const formHeaders = typeof formData?.getHeaders === 'function' && formData?.getHeaders() || {}
 
     const headers = Object.entries({
diff --git a/src/client/resource/models/ErrorDetail.ts b/src/client/resource/models/ErrorDetail.ts
index eba50ab..69dd37e 100644
--- a/src/client/resource/models/ErrorDetail.ts
+++ b/src/client/resource/models/ErrorDetail.ts
@@ -2,7 +2,6 @@
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
-
 /**
  * Schema for a error due to a rejected request.
  */
diff --git a/src/client/resource/models/HTTPValidationError.ts b/src/client/resource/models/HTTPValidationError.ts
index c0bcc87..892e425 100644
--- a/src/client/resource/models/HTTPValidationError.ts
+++ b/src/client/resource/models/HTTPValidationError.ts
@@ -2,9 +2,7 @@
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
-
 import type { ValidationError } from './ValidationError';
-
 export type HTTPValidationError = {
     detail?: Array<ValidationError>;
 };
diff --git a/src/client/resource/models/ResourceIn.ts b/src/client/resource/models/ResourceIn.ts
index c9c83e3..bb3eddc 100644
--- a/src/client/resource/models/ResourceIn.ts
+++ b/src/client/resource/models/ResourceIn.ts
@@ -2,7 +2,6 @@
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
-
 export type ResourceIn = {
     /**
      * Short tag describing the version of the resource
diff --git a/src/client/resource/models/ResourceOut.ts b/src/client/resource/models/ResourceOut.ts
index 2d1a2b0..dcb3455 100644
--- a/src/client/resource/models/ResourceOut.ts
+++ b/src/client/resource/models/ResourceOut.ts
@@ -2,9 +2,7 @@
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
-
 import type { ResourceVersionOut } from './ResourceVersionOut';
-
 export type ResourceOut = {
     /**
      * Short Name for the resource
diff --git a/src/client/resource/models/ResourceVersionIn.ts b/src/client/resource/models/ResourceVersionIn.ts
index 4adef89..68eb986 100644
--- a/src/client/resource/models/ResourceVersionIn.ts
+++ b/src/client/resource/models/ResourceVersionIn.ts
@@ -2,7 +2,6 @@
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
-
 export type ResourceVersionIn = {
     /**
      * Short tag describing the version of the resource
diff --git a/src/client/resource/models/ResourceVersionOut.ts b/src/client/resource/models/ResourceVersionOut.ts
index 3723577..7ab1dda 100644
--- a/src/client/resource/models/ResourceVersionOut.ts
+++ b/src/client/resource/models/ResourceVersionOut.ts
@@ -2,9 +2,7 @@
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
-
 import type { Status } from './Status';
-
 export type ResourceVersionOut = {
     /**
      * Short tag describing the version of the resource
diff --git a/src/client/resource/models/Status.ts b/src/client/resource/models/Status.ts
index 7bd78cb..51aaaaa 100644
--- a/src/client/resource/models/Status.ts
+++ b/src/client/resource/models/Status.ts
@@ -2,7 +2,6 @@
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
-
 /**
  * Enumeration for the possible status of a resource version.
  */
diff --git a/src/client/resource/models/ValidationError.ts b/src/client/resource/models/ValidationError.ts
index 18997ec..f2ff49a 100644
--- a/src/client/resource/models/ValidationError.ts
+++ b/src/client/resource/models/ValidationError.ts
@@ -2,7 +2,6 @@
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
-
 export type ValidationError = {
     loc: Array<(string | number)>;
     msg: string;
diff --git a/src/client/resource/services/ResourceService.ts b/src/client/resource/services/ResourceService.ts
index 7d00e57..2c182a2 100644
--- a/src/client/resource/services/ResourceService.ts
+++ b/src/client/resource/services/ResourceService.ts
@@ -5,13 +5,10 @@
 import type { ResourceIn } from '../models/ResourceIn';
 import type { ResourceOut } from '../models/ResourceOut';
 import type { Status } from '../models/Status';
-
 import type { CancelablePromise } from '../core/CancelablePromise';
 import { OpenAPI } from '../core/OpenAPI';
 import { request as __request } from '../core/request';
-
 export class ResourceService {
-
     /**
      * List resources
      * List all resources.
@@ -24,9 +21,9 @@ export class ResourceService {
      * @throws ApiError
      */
     public static resourceListResources(
-        maintainerId?: (string | null),
-        versionStatus?: (Array<Status> | null),
-        nameSubstring?: (string | null),
+        maintainerId?: string,
+        versionStatus?: Array<Status>,
+        nameSubstring?: string,
     ): CancelablePromise<Array<ResourceOut>> {
         return __request(OpenAPI, {
             method: 'GET',
@@ -44,7 +41,6 @@ export class ResourceService {
             },
         });
     }
-
     /**
      * Request a new resource
      * Request a new resources.
@@ -54,7 +50,7 @@ export class ResourceService {
      * @returns ResourceOut Successful Response
      * @throws ApiError
      */
-    public static resourceRequestResource(
+    public static resourceCreateResource(
         requestBody: ResourceIn,
     ): CancelablePromise<ResourceOut> {
         return __request(OpenAPI, {
@@ -70,7 +66,6 @@ export class ResourceService {
             },
         });
     }
-
     /**
      * Get a resource
      * Get a specific resource.
@@ -83,7 +78,7 @@ export class ResourceService {
      */
     public static resourceGetResource(
         rid: string,
-        versionStatus?: (Array<Status> | null),
+        versionStatus?: Array<Status>,
     ): CancelablePromise<ResourceOut> {
         return __request(OpenAPI, {
             method: 'GET',
@@ -102,7 +97,6 @@ export class ResourceService {
             },
         });
     }
-
     /**
      * Delete a resource
      * Delete a resources.
@@ -129,5 +123,4 @@ export class ResourceService {
             },
         });
     }
-
 }
diff --git a/src/client/resource/services/ResourceVersionService.ts b/src/client/resource/services/ResourceVersionService.ts
index dd1fa68..804d721 100644
--- a/src/client/resource/services/ResourceVersionService.ts
+++ b/src/client/resource/services/ResourceVersionService.ts
@@ -5,13 +5,10 @@
 import type { ResourceVersionIn } from '../models/ResourceVersionIn';
 import type { ResourceVersionOut } from '../models/ResourceVersionOut';
 import type { Status } from '../models/Status';
-
 import type { CancelablePromise } from '../core/CancelablePromise';
 import { OpenAPI } from '../core/OpenAPI';
 import { request as __request } from '../core/request';
-
 export class ResourceVersionService {
-
     /**
      * List versions of a resource
      * List all the resource version for a specific resource.
@@ -24,7 +21,7 @@ export class ResourceVersionService {
      */
     public static resourceVersionListResourceVersions(
         rid: string,
-        versionStatus?: (Array<Status> | null),
+        versionStatus?: Array<Status>,
     ): CancelablePromise<Array<ResourceVersionOut>> {
         return __request(OpenAPI, {
             method: 'GET',
@@ -43,7 +40,6 @@ export class ResourceVersionService {
             },
         });
     }
-
     /**
      * Request new version of a resource
      * Request a new resource version.
@@ -74,7 +70,6 @@ export class ResourceVersionService {
             },
         });
     }
-
     /**
      * Get version of a resource
      * Get a specific resource version for a specific resource.
@@ -105,7 +100,6 @@ export class ResourceVersionService {
             },
         });
     }
-
     /**
      * Request resource version synchronization
      * Request the synchronization of a resource version to the cluster.
@@ -135,7 +129,6 @@ export class ResourceVersionService {
             },
         });
     }
-
     /**
      * Synchronize resource version with cluster
      * Synchronize the resource version to the cluster.
@@ -165,7 +158,6 @@ export class ResourceVersionService {
             },
         });
     }
-
     /**
      * Set resource version to latest
      * Set the resource version as the latest version.
@@ -195,7 +187,6 @@ export class ResourceVersionService {
             },
         });
     }
-
     /**
      * Delete resource version on cluster
      * Delete the resource version on the cluster.
@@ -225,7 +216,6 @@ export class ResourceVersionService {
             },
         });
     }
-
     /**
      * Delete resource version in S3
      * Delete the resource version in the S3 bucket.
@@ -255,5 +245,4 @@ export class ResourceVersionService {
             },
         });
     }
-
 }
diff --git a/src/client/s3proxy/core/CancelablePromise.ts b/src/client/s3proxy/core/CancelablePromise.ts
index 55fef85..eb02246 100644
--- a/src/client/s3proxy/core/CancelablePromise.ts
+++ b/src/client/s3proxy/core/CancelablePromise.ts
@@ -51,7 +51,7 @@ export class CancelablePromise<T> implements Promise<T> {
                     return;
                 }
                 this.#isResolved = true;
-                this.#resolve?.(value);
+                if (this.#resolve) this.#resolve(value);
             };
 
             const onReject = (reason?: any): void => {
@@ -59,7 +59,7 @@ export class CancelablePromise<T> implements Promise<T> {
                     return;
                 }
                 this.#isRejected = true;
-                this.#reject?.(reason);
+                if (this.#reject) this.#reject(reason);
             };
 
             const onCancel = (cancelHandler: () => void): void => {
@@ -122,7 +122,7 @@ export class CancelablePromise<T> implements Promise<T> {
             }
         }
         this.#cancelHandlers.length = 0;
-        this.#reject?.(new CancelError('Request aborted'));
+        if (this.#reject) this.#reject(new CancelError('Request aborted'));
     }
 
     public get isCancelled(): boolean {
diff --git a/src/client/s3proxy/core/request.ts b/src/client/s3proxy/core/request.ts
index 1142d43..c6a0602 100644
--- a/src/client/s3proxy/core/request.ts
+++ b/src/client/s3proxy/core/request.ts
@@ -145,10 +145,13 @@ export const resolve = async <T>(options: ApiRequestOptions, resolver?: T | Reso
 };
 
 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);
-    const additionalHeaders = await resolve(options, config.HEADERS);
+    const [token, username, password, additionalHeaders] = await Promise.all([
+        resolve(options, config.TOKEN),
+        resolve(options, config.USERNAME),
+        resolve(options, config.PASSWORD),
+        resolve(options, config.HEADERS),
+    ]);
+
     const formHeaders = typeof formData?.getHeaders === 'function' && formData?.getHeaders() || {}
 
     const headers = Object.entries({
diff --git a/src/client/s3proxy/models/BucketIn.ts b/src/client/s3proxy/models/BucketIn.ts
index 96ff7b4..9505a64 100644
--- a/src/client/s3proxy/models/BucketIn.ts
+++ b/src/client/s3proxy/models/BucketIn.ts
@@ -2,7 +2,6 @@
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
-
 /**
  * Schema for creating a new bucket.
  */
diff --git a/src/client/s3proxy/models/BucketOut.ts b/src/client/s3proxy/models/BucketOut.ts
index 5b38801..cbb67fb 100644
--- a/src/client/s3proxy/models/BucketOut.ts
+++ b/src/client/s3proxy/models/BucketOut.ts
@@ -2,9 +2,7 @@
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
-
 import type { Constraint } from './Constraint';
-
 /**
  * Schema for answering a request with a bucket.
  */
diff --git a/src/client/s3proxy/models/BucketPermissionIn.ts b/src/client/s3proxy/models/BucketPermissionIn.ts
index 06bd223..e0e3825 100644
--- a/src/client/s3proxy/models/BucketPermissionIn.ts
+++ b/src/client/s3proxy/models/BucketPermissionIn.ts
@@ -2,9 +2,7 @@
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
-
 import type { Permission } from './Permission';
-
 export type BucketPermissionIn = {
     /**
      * Start date of permission as UNIX timestamp
diff --git a/src/client/s3proxy/models/BucketPermissionOut.ts b/src/client/s3proxy/models/BucketPermissionOut.ts
index 38ac025..ec3c79d 100644
--- a/src/client/s3proxy/models/BucketPermissionOut.ts
+++ b/src/client/s3proxy/models/BucketPermissionOut.ts
@@ -2,9 +2,7 @@
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
-
 import type { Permission } from './Permission';
-
 /**
  * Schema for the bucket permissions.
  */
diff --git a/src/client/s3proxy/models/BucketPermissionParameters.ts b/src/client/s3proxy/models/BucketPermissionParameters.ts
index 4d8ef97..47c7576 100644
--- a/src/client/s3proxy/models/BucketPermissionParameters.ts
+++ b/src/client/s3proxy/models/BucketPermissionParameters.ts
@@ -2,9 +2,7 @@
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
-
 import type { Permission } from './Permission';
-
 /**
  * Schema for the parameters of a bucket permission.
  */
diff --git a/src/client/s3proxy/models/BucketType.ts b/src/client/s3proxy/models/BucketType.ts
index b096c70..8001a27 100644
--- a/src/client/s3proxy/models/BucketType.ts
+++ b/src/client/s3proxy/models/BucketType.ts
@@ -2,7 +2,6 @@
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
-
 /**
  * Enumeration for the type of buckets to fetch from the DB
  *
diff --git a/src/client/s3proxy/models/Constraint.ts b/src/client/s3proxy/models/Constraint.ts
index afa995a..9904322 100644
--- a/src/client/s3proxy/models/Constraint.ts
+++ b/src/client/s3proxy/models/Constraint.ts
@@ -2,7 +2,6 @@
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
-
 /**
  * Enumeration for the possible permission on a bucket.
  */
diff --git a/src/client/s3proxy/models/ErrorDetail.ts b/src/client/s3proxy/models/ErrorDetail.ts
index eba50ab..69dd37e 100644
--- a/src/client/s3proxy/models/ErrorDetail.ts
+++ b/src/client/s3proxy/models/ErrorDetail.ts
@@ -2,7 +2,6 @@
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
-
 /**
  * Schema for a error due to a rejected request.
  */
diff --git a/src/client/s3proxy/models/HTTPValidationError.ts b/src/client/s3proxy/models/HTTPValidationError.ts
index c0bcc87..892e425 100644
--- a/src/client/s3proxy/models/HTTPValidationError.ts
+++ b/src/client/s3proxy/models/HTTPValidationError.ts
@@ -2,9 +2,7 @@
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
-
 import type { ValidationError } from './ValidationError';
-
 export type HTTPValidationError = {
     detail?: Array<ValidationError>;
 };
diff --git a/src/client/s3proxy/models/Permission.ts b/src/client/s3proxy/models/Permission.ts
index 00592a4..33aebf7 100644
--- a/src/client/s3proxy/models/Permission.ts
+++ b/src/client/s3proxy/models/Permission.ts
@@ -2,7 +2,6 @@
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
-
 /**
  * Enumeration for the possible permission on a bucket.
  */
diff --git a/src/client/s3proxy/models/PermissionStatus.ts b/src/client/s3proxy/models/PermissionStatus.ts
index d51e092..0d858f0 100644
--- a/src/client/s3proxy/models/PermissionStatus.ts
+++ b/src/client/s3proxy/models/PermissionStatus.ts
@@ -2,7 +2,6 @@
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
-
 /**
  * Status of a bucket permission. Can be either `ACTIVE` or `INACTIVE`. A permission can only get `INACTIVE` if the
  * permission itself has a time limit and the current time is not in the timespan.
diff --git a/src/client/s3proxy/models/S3Key.ts b/src/client/s3proxy/models/S3Key.ts
index cc9f01a..b3c30da 100644
--- a/src/client/s3proxy/models/S3Key.ts
+++ b/src/client/s3proxy/models/S3Key.ts
@@ -2,7 +2,6 @@
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
-
 /**
  * Schema for a S3 key associated with a user.
  */
diff --git a/src/client/s3proxy/models/ValidationError.ts b/src/client/s3proxy/models/ValidationError.ts
index 18997ec..f2ff49a 100644
--- a/src/client/s3proxy/models/ValidationError.ts
+++ b/src/client/s3proxy/models/ValidationError.ts
@@ -2,7 +2,6 @@
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
-
 export type ValidationError = {
     loc: Array<(string | number)>;
     msg: string;
diff --git a/src/client/s3proxy/services/BucketPermissionService.ts b/src/client/s3proxy/services/BucketPermissionService.ts
index 3b55d1b..d5119dc 100644
--- a/src/client/s3proxy/services/BucketPermissionService.ts
+++ b/src/client/s3proxy/services/BucketPermissionService.ts
@@ -7,13 +7,10 @@ import type { BucketPermissionOut } from '../models/BucketPermissionOut';
 import type { BucketPermissionParameters } from '../models/BucketPermissionParameters';
 import type { Permission } from '../models/Permission';
 import type { PermissionStatus } from '../models/PermissionStatus';
-
 import type { CancelablePromise } from '../core/CancelablePromise';
 import { OpenAPI } from '../core/OpenAPI';
 import { request as __request } from '../core/request';
-
 export class BucketPermissionService {
-
     /**
      * Get permission for bucket and user combination.
      * Get the bucket permissions for the specific combination of bucket and user.
@@ -46,7 +43,6 @@ export class BucketPermissionService {
             },
         });
     }
-
     /**
      * Delete a bucket permission
      * Delete the bucket permissions for the specific combination of bucket and user.
@@ -79,7 +75,6 @@ export class BucketPermissionService {
             },
         });
     }
-
     /**
      * Update a bucket permission
      * Update a permission for a bucket and user.
@@ -114,7 +109,6 @@ export class BucketPermissionService {
             },
         });
     }
-
     /**
      * Get all permissions for a bucket.
      * List all the bucket permissions for the given bucket.
@@ -150,7 +144,6 @@ export class BucketPermissionService {
             },
         });
     }
-
     /**
      * Get all permissions for a user.
      * List all the bucket permissions for the given user.
@@ -186,7 +179,6 @@ export class BucketPermissionService {
             },
         });
     }
-
     /**
      * Create a permission.
      * Create a permission for a bucket and user.
@@ -212,5 +204,4 @@ export class BucketPermissionService {
             },
         });
     }
-
 }
diff --git a/src/client/s3proxy/services/BucketService.ts b/src/client/s3proxy/services/BucketService.ts
index d51b668..92cee99 100644
--- a/src/client/s3proxy/services/BucketService.ts
+++ b/src/client/s3proxy/services/BucketService.ts
@@ -5,13 +5,10 @@
 import type { BucketIn } from '../models/BucketIn';
 import type { BucketOut } from '../models/BucketOut';
 import type { BucketType } from '../models/BucketType';
-
 import type { CancelablePromise } from '../core/CancelablePromise';
 import { OpenAPI } from '../core/OpenAPI';
 import { request as __request } from '../core/request';
-
 export class BucketService {
-
     /**
      * List buckets of user
      * List all the buckets in the system or of the desired user where the user has READ permissions for.
@@ -41,7 +38,6 @@ export class BucketService {
             },
         });
     }
-
     /**
      * Create a bucket for the current user
      * Create a bucket for the current user.
@@ -71,7 +67,6 @@ export class BucketService {
             },
         });
     }
-
     /**
      * Get a bucket by its name
      * Get a bucket by its name if the current user has READ permissions for the bucket.
@@ -99,7 +94,6 @@ export class BucketService {
             },
         });
     }
-
     /**
      * Delete a bucket
      * Delete a bucket by its name. Only the owner of the bucket can delete the bucket.
@@ -132,5 +126,4 @@ export class BucketService {
             },
         });
     }
-
 }
diff --git a/src/client/s3proxy/services/S3KeyService.ts b/src/client/s3proxy/services/S3KeyService.ts
index c8e048b..732fd68 100644
--- a/src/client/s3proxy/services/S3KeyService.ts
+++ b/src/client/s3proxy/services/S3KeyService.ts
@@ -3,13 +3,10 @@
 /* tslint:disable */
 /* eslint-disable */
 import type { S3Key } from '../models/S3Key';
-
 import type { CancelablePromise } from '../core/CancelablePromise';
 import { OpenAPI } from '../core/OpenAPI';
 import { request as __request } from '../core/request';
-
 export class S3KeyService {
-
     /**
      * Get the S3 Access keys from a user
      * Get all the S3 Access keys for a specific user.
@@ -36,7 +33,6 @@ export class S3KeyService {
             },
         });
     }
-
     /**
      * Create a Access key for a user
      * Create a S3 Access key for a specific user.
@@ -63,7 +59,6 @@ export class S3KeyService {
             },
         });
     }
-
     /**
      * Get a specific S3 Access key from a user
      * Get a specific S3 Access Key for a specific user.
@@ -93,7 +88,6 @@ export class S3KeyService {
             },
         });
     }
-
     /**
      * Delete a specific S3 Access key from a user
      * Delete a specific S3 Access key for a specific user.
@@ -123,5 +117,4 @@ export class S3KeyService {
             },
         });
     }
-
 }
diff --git a/src/client/workflow/core/CancelablePromise.ts b/src/client/workflow/core/CancelablePromise.ts
index 55fef85..eb02246 100644
--- a/src/client/workflow/core/CancelablePromise.ts
+++ b/src/client/workflow/core/CancelablePromise.ts
@@ -51,7 +51,7 @@ export class CancelablePromise<T> implements Promise<T> {
                     return;
                 }
                 this.#isResolved = true;
-                this.#resolve?.(value);
+                if (this.#resolve) this.#resolve(value);
             };
 
             const onReject = (reason?: any): void => {
@@ -59,7 +59,7 @@ export class CancelablePromise<T> implements Promise<T> {
                     return;
                 }
                 this.#isRejected = true;
-                this.#reject?.(reason);
+                if (this.#reject) this.#reject(reason);
             };
 
             const onCancel = (cancelHandler: () => void): void => {
@@ -122,7 +122,7 @@ export class CancelablePromise<T> implements Promise<T> {
             }
         }
         this.#cancelHandlers.length = 0;
-        this.#reject?.(new CancelError('Request aborted'));
+        if (this.#reject) this.#reject(new CancelError('Request aborted'));
     }
 
     public get isCancelled(): boolean {
diff --git a/src/client/workflow/core/request.ts b/src/client/workflow/core/request.ts
index 1142d43..c6a0602 100644
--- a/src/client/workflow/core/request.ts
+++ b/src/client/workflow/core/request.ts
@@ -145,10 +145,13 @@ export const resolve = async <T>(options: ApiRequestOptions, resolver?: T | Reso
 };
 
 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);
-    const additionalHeaders = await resolve(options, config.HEADERS);
+    const [token, username, password, additionalHeaders] = await Promise.all([
+        resolve(options, config.TOKEN),
+        resolve(options, config.USERNAME),
+        resolve(options, config.PASSWORD),
+        resolve(options, config.HEADERS),
+    ]);
+
     const formHeaders = typeof formData?.getHeaders === 'function' && formData?.getHeaders() || {}
 
     const headers = Object.entries({
diff --git a/src/client/workflow/models/AnonymizedWorkflowExecution.ts b/src/client/workflow/models/AnonymizedWorkflowExecution.ts
index eb4085f..11497f7 100644
--- a/src/client/workflow/models/AnonymizedWorkflowExecution.ts
+++ b/src/client/workflow/models/AnonymizedWorkflowExecution.ts
@@ -2,9 +2,7 @@
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
-
 import type { WorkflowExecutionStatus } from './WorkflowExecutionStatus';
-
 export type AnonymizedWorkflowExecution = {
     /**
      * ID of the workflow execution
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
index 208b0c2..fa8dd9e 100644
--- 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
@@ -2,7 +2,6 @@
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
-
 export type Body_Workflow_Version_upload_workflow_version_icon = {
     /**
      * Optional Icon for the Workflow.
diff --git a/src/client/workflow/models/DevWorkflowExecutionIn.ts b/src/client/workflow/models/DevWorkflowExecutionIn.ts
index e8c8a52..ad6cbd9 100644
--- a/src/client/workflow/models/DevWorkflowExecutionIn.ts
+++ b/src/client/workflow/models/DevWorkflowExecutionIn.ts
@@ -2,9 +2,7 @@
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
-
 import type { WorkflowModeIn } from './WorkflowModeIn';
-
 export type DevWorkflowExecutionIn = {
     /**
      * Parameters for this workflow
diff --git a/src/client/workflow/models/DocumentationEnum.ts b/src/client/workflow/models/DocumentationEnum.ts
index 1ae0485..2f5b3e3 100644
--- a/src/client/workflow/models/DocumentationEnum.ts
+++ b/src/client/workflow/models/DocumentationEnum.ts
@@ -2,7 +2,6 @@
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
-
 export enum DocumentationEnum {
     USAGE = 'usage',
     INPUT = 'input',
diff --git a/src/client/workflow/models/ErrorDetail.ts b/src/client/workflow/models/ErrorDetail.ts
index eba50ab..69dd37e 100644
--- a/src/client/workflow/models/ErrorDetail.ts
+++ b/src/client/workflow/models/ErrorDetail.ts
@@ -2,7 +2,6 @@
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
-
 /**
  * Schema for a error due to a rejected request.
  */
diff --git a/src/client/workflow/models/HTTPValidationError.ts b/src/client/workflow/models/HTTPValidationError.ts
index c0bcc87..892e425 100644
--- a/src/client/workflow/models/HTTPValidationError.ts
+++ b/src/client/workflow/models/HTTPValidationError.ts
@@ -2,9 +2,7 @@
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
-
 import type { ValidationError } from './ValidationError';
-
 export type HTTPValidationError = {
     detail?: Array<ValidationError>;
 };
diff --git a/src/client/workflow/models/IconUpdateOut.ts b/src/client/workflow/models/IconUpdateOut.ts
index 0499796..1210a93 100644
--- a/src/client/workflow/models/IconUpdateOut.ts
+++ b/src/client/workflow/models/IconUpdateOut.ts
@@ -2,7 +2,6 @@
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
-
 export type IconUpdateOut = {
     /**
      * URL to the uploaded icon
diff --git a/src/client/workflow/models/Status.ts b/src/client/workflow/models/Status.ts
index 8da66ea..fd918bd 100644
--- a/src/client/workflow/models/Status.ts
+++ b/src/client/workflow/models/Status.ts
@@ -2,7 +2,6 @@
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
-
 /**
  * Enumeration for the possible status of a workflow version.
  */
diff --git a/src/client/workflow/models/ValidationError.ts b/src/client/workflow/models/ValidationError.ts
index 18997ec..f2ff49a 100644
--- a/src/client/workflow/models/ValidationError.ts
+++ b/src/client/workflow/models/ValidationError.ts
@@ -2,7 +2,6 @@
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
-
 export type ValidationError = {
     loc: Array<(string | number)>;
     msg: string;
diff --git a/src/client/workflow/models/WorkflowCredentialsIn.ts b/src/client/workflow/models/WorkflowCredentialsIn.ts
index 37723a3..a3b3c10 100644
--- a/src/client/workflow/models/WorkflowCredentialsIn.ts
+++ b/src/client/workflow/models/WorkflowCredentialsIn.ts
@@ -2,7 +2,6 @@
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
-
 export type WorkflowCredentialsIn = {
     /**
      * Token to access the content git repository
diff --git a/src/client/workflow/models/WorkflowCredentialsOut.ts b/src/client/workflow/models/WorkflowCredentialsOut.ts
index 8a447b4..371252c 100644
--- a/src/client/workflow/models/WorkflowCredentialsOut.ts
+++ b/src/client/workflow/models/WorkflowCredentialsOut.ts
@@ -2,7 +2,6 @@
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
-
 export type WorkflowCredentialsOut = {
     /**
      * Token to access the content git repository
diff --git a/src/client/workflow/models/WorkflowExecutionIn.ts b/src/client/workflow/models/WorkflowExecutionIn.ts
index 3532aa8..223447a 100644
--- a/src/client/workflow/models/WorkflowExecutionIn.ts
+++ b/src/client/workflow/models/WorkflowExecutionIn.ts
@@ -2,7 +2,6 @@
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
-
 export type WorkflowExecutionIn = {
     /**
      * Parameters for this workflow
diff --git a/src/client/workflow/models/WorkflowExecutionOut.ts b/src/client/workflow/models/WorkflowExecutionOut.ts
index a804c6a..dc3e981 100644
--- a/src/client/workflow/models/WorkflowExecutionOut.ts
+++ b/src/client/workflow/models/WorkflowExecutionOut.ts
@@ -2,9 +2,7 @@
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
-
 import type { WorkflowExecutionStatus } from './WorkflowExecutionStatus';
-
 export type WorkflowExecutionOut = {
     /**
      * Workflow version git commit hash
diff --git a/src/client/workflow/models/WorkflowExecutionStatus.ts b/src/client/workflow/models/WorkflowExecutionStatus.ts
index dee8c8f..6b92108 100644
--- a/src/client/workflow/models/WorkflowExecutionStatus.ts
+++ b/src/client/workflow/models/WorkflowExecutionStatus.ts
@@ -2,7 +2,6 @@
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
-
 /**
  * Enumeration for the status on a workflow execution.
  */
diff --git a/src/client/workflow/models/WorkflowIn.ts b/src/client/workflow/models/WorkflowIn.ts
index 9367556..c5322ce 100644
--- a/src/client/workflow/models/WorkflowIn.ts
+++ b/src/client/workflow/models/WorkflowIn.ts
@@ -2,9 +2,7 @@
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
-
 import type { WorkflowModeIn } from './WorkflowModeIn';
-
 export type WorkflowIn = {
     /**
      * Short descriptive name of the workflow
diff --git a/src/client/workflow/models/WorkflowModeIn.ts b/src/client/workflow/models/WorkflowModeIn.ts
index a50b038..7d8d2d4 100644
--- a/src/client/workflow/models/WorkflowModeIn.ts
+++ b/src/client/workflow/models/WorkflowModeIn.ts
@@ -2,7 +2,6 @@
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
-
 export type WorkflowModeIn = {
     /**
      * Path to the alternative parameter schema
diff --git a/src/client/workflow/models/WorkflowModeOut.ts b/src/client/workflow/models/WorkflowModeOut.ts
index 1d52e08..d3b61d5 100644
--- a/src/client/workflow/models/WorkflowModeOut.ts
+++ b/src/client/workflow/models/WorkflowModeOut.ts
@@ -2,7 +2,6 @@
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
-
 export type WorkflowModeOut = {
     /**
      * Path to the alternative parameter schema
diff --git a/src/client/workflow/models/WorkflowOut.ts b/src/client/workflow/models/WorkflowOut.ts
index a3d055f..90fbae3 100644
--- a/src/client/workflow/models/WorkflowOut.ts
+++ b/src/client/workflow/models/WorkflowOut.ts
@@ -2,9 +2,7 @@
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
-
 import type { WorkflowVersion } from './WorkflowVersion';
-
 export type WorkflowOut = {
     /**
      * Short descriptive name of the workflow
diff --git a/src/client/workflow/models/WorkflowStatistic.ts b/src/client/workflow/models/WorkflowStatistic.ts
index 4b7fbad..d84e2dc 100644
--- a/src/client/workflow/models/WorkflowStatistic.ts
+++ b/src/client/workflow/models/WorkflowStatistic.ts
@@ -2,7 +2,6 @@
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
-
 export type WorkflowStatistic = {
     /**
      * Day of the datapoint
diff --git a/src/client/workflow/models/WorkflowUpdate.ts b/src/client/workflow/models/WorkflowUpdate.ts
index 6aa6f18..879f4bc 100644
--- a/src/client/workflow/models/WorkflowUpdate.ts
+++ b/src/client/workflow/models/WorkflowUpdate.ts
@@ -2,9 +2,7 @@
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
-
 import type { WorkflowModeIn } from './WorkflowModeIn';
-
 export type WorkflowUpdate = {
     /**
      * Version of the Workflow. Should follow semantic versioning
diff --git a/src/client/workflow/models/WorkflowVersion.ts b/src/client/workflow/models/WorkflowVersion.ts
index 26b656f..d27d809 100644
--- a/src/client/workflow/models/WorkflowVersion.ts
+++ b/src/client/workflow/models/WorkflowVersion.ts
@@ -2,9 +2,7 @@
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
-
 import type { Status } from './Status';
-
 export type WorkflowVersion = {
     /**
      * Status of the workflow version
diff --git a/src/client/workflow/models/WorkflowVersionStatus.ts b/src/client/workflow/models/WorkflowVersionStatus.ts
index 0c66d1d..765e688 100644
--- a/src/client/workflow/models/WorkflowVersionStatus.ts
+++ b/src/client/workflow/models/WorkflowVersionStatus.ts
@@ -2,9 +2,7 @@
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
-
 import type { Status } from './Status';
-
 export type WorkflowVersionStatus = {
     /**
      * Status of the workflow version
diff --git a/src/client/workflow/services/WorkflowCredentialsService.ts b/src/client/workflow/services/WorkflowCredentialsService.ts
index 1f9f891..cf305fa 100644
--- a/src/client/workflow/services/WorkflowCredentialsService.ts
+++ b/src/client/workflow/services/WorkflowCredentialsService.ts
@@ -4,13 +4,10 @@
 /* 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.
@@ -37,7 +34,6 @@ export class WorkflowCredentialsService {
             },
         });
     }
-
     /**
      * Update the credentials of a workflow
      * Update the credentials for the repository of a workflow.
@@ -68,7 +64,6 @@ export class WorkflowCredentialsService {
             },
         });
     }
-
     /**
      * Delete the credentials of a workflow
      * Delete the credentials for the repository of a workflow.
@@ -95,5 +90,4 @@ export class WorkflowCredentialsService {
             },
         });
     }
-
 }
diff --git a/src/client/workflow/services/WorkflowExecutionService.ts b/src/client/workflow/services/WorkflowExecutionService.ts
index 199ba7b..db4e486 100644
--- a/src/client/workflow/services/WorkflowExecutionService.ts
+++ b/src/client/workflow/services/WorkflowExecutionService.ts
@@ -6,13 +6,10 @@ import type { DevWorkflowExecutionIn } from '../models/DevWorkflowExecutionIn';
 import type { WorkflowExecutionIn } from '../models/WorkflowExecutionIn';
 import type { WorkflowExecutionOut } from '../models/WorkflowExecutionOut';
 import type { WorkflowExecutionStatus } from '../models/WorkflowExecutionStatus';
-
 import type { CancelablePromise } from '../core/CancelablePromise';
 import { OpenAPI } from '../core/OpenAPI';
 import { request as __request } from '../core/request';
-
 export class WorkflowExecutionService {
-
     /**
      * Start a new workflow execution
      * Start a new workflow execution. Workflow versions wit status `DEPRECATED` or `DENIED` can't be started.
@@ -39,7 +36,6 @@ export class WorkflowExecutionService {
             },
         });
     }
-
     /**
      * Get all workflow executions
      * Get all workflow executions.
@@ -73,7 +69,6 @@ export class WorkflowExecutionService {
             },
         });
     }
-
     /**
      * Start a workflow execution with arbitrary git repository
      * Start a new workflow execution from an arbitrary git repository.
@@ -103,7 +98,6 @@ export class WorkflowExecutionService {
             },
         });
     }
-
     /**
      * Get a workflow execution
      * Get a specific workflow execution.
@@ -131,7 +125,6 @@ export class WorkflowExecutionService {
             },
         });
     }
-
     /**
      * Delete a workflow execution
      * Delete a specific workflow execution.
@@ -159,7 +152,6 @@ export class WorkflowExecutionService {
             },
         });
     }
-
     /**
      * Get the parameters of a workflow execution
      * Get the parameters of a specific workflow execution.
@@ -187,7 +179,6 @@ export class WorkflowExecutionService {
             },
         });
     }
-
     /**
      * Cancel a workflow execution
      * Cancel a running workflow execution.
@@ -215,5 +206,4 @@ export class WorkflowExecutionService {
             },
         });
     }
-
 }
diff --git a/src/client/workflow/services/WorkflowModeService.ts b/src/client/workflow/services/WorkflowModeService.ts
index f5e1926..f0fa89d 100644
--- a/src/client/workflow/services/WorkflowModeService.ts
+++ b/src/client/workflow/services/WorkflowModeService.ts
@@ -3,13 +3,10 @@
 /* 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 {
-
     /**
      * Get workflow mode
      * Get a workflow mode
@@ -36,5 +33,4 @@ export class WorkflowModeService {
             },
         });
     }
-
 }
diff --git a/src/client/workflow/services/WorkflowService.ts b/src/client/workflow/services/WorkflowService.ts
index b173a10..9dc2f2c 100644
--- a/src/client/workflow/services/WorkflowService.ts
+++ b/src/client/workflow/services/WorkflowService.ts
@@ -13,13 +13,10 @@ import type { WorkflowStatistic } from '../models/WorkflowStatistic';
 import type { WorkflowUpdate } from '../models/WorkflowUpdate';
 import type { WorkflowVersion } from '../models/WorkflowVersion';
 import type { WorkflowVersionStatus } from '../models/WorkflowVersionStatus';
-
 import type { CancelablePromise } from '../core/CancelablePromise';
 import { OpenAPI } from '../core/OpenAPI';
 import { request as __request } from '../core/request';
-
 export class WorkflowService {
-
     /**
      * List workflows
      * List all workflows.
@@ -52,7 +49,6 @@ export class WorkflowService {
             },
         });
     }
-
     /**
      * Create a new workflow
      * Create a new workflow.
@@ -82,7 +78,6 @@ export class WorkflowService {
             },
         });
     }
-
     /**
      * Get anonymized workflow execution
      * Get the workflow executions with meta information and anonymized user IDs.
@@ -119,7 +114,6 @@ export class WorkflowService {
             },
         });
     }
-
     /**
      * Get a workflow
      * Get a specific workflow.
@@ -151,7 +145,6 @@ export class WorkflowService {
             },
         });
     }
-
     /**
      * Delete a workflow
      * Delete a workflow.
@@ -178,7 +171,6 @@ export class WorkflowService {
             },
         });
     }
-
     /**
      * Get statistics for a workflow
      * Get the number of started workflow per day.
@@ -205,7 +197,6 @@ export class WorkflowService {
             },
         });
     }
-
     /**
      * Update a workflow
      * Create a new workflow version.
@@ -236,7 +227,6 @@ export class WorkflowService {
             },
         });
     }
-
     /**
      * Get all versions of a workflow
      * List all versions of a Workflow.
@@ -268,7 +258,6 @@ export class WorkflowService {
             },
         });
     }
-
     /**
      * Get a workflow version
      * Get a specific version of a workflow.
@@ -299,7 +288,6 @@ export class WorkflowService {
             },
         });
     }
-
     /**
      * Update status of workflow version
      * Update the status of a workflow version.
@@ -333,7 +321,6 @@ export class WorkflowService {
             },
         });
     }
-
     /**
      * Deprecate a workflow version
      * Deprecate a workflow version.
@@ -364,7 +351,6 @@ export class WorkflowService {
             },
         });
     }
-
     /**
      * Fetch documentation for a workflow version
      * Get the documentation for a specific workflow version.
@@ -403,7 +389,6 @@ export class WorkflowService {
             },
         });
     }
-
     /**
      * Upload icon for workflow version
      * Upload an icon for the workflow version and returns the new icon URL.
@@ -437,7 +422,6 @@ export class WorkflowService {
             },
         });
     }
-
     /**
      * Delete icon of workflow version
      * Delete the icon of the workflow version.
@@ -467,5 +451,4 @@ export class WorkflowService {
             },
         });
     }
-
 }
diff --git a/src/client/workflow/services/WorkflowVersionService.ts b/src/client/workflow/services/WorkflowVersionService.ts
index 9553b05..47eb077 100644
--- a/src/client/workflow/services/WorkflowVersionService.ts
+++ b/src/client/workflow/services/WorkflowVersionService.ts
@@ -8,13 +8,10 @@ import type { IconUpdateOut } from '../models/IconUpdateOut';
 import type { Status } from '../models/Status';
 import type { WorkflowVersion } from '../models/WorkflowVersion';
 import type { WorkflowVersionStatus } from '../models/WorkflowVersionStatus';
-
 import type { CancelablePromise } from '../core/CancelablePromise';
 import { OpenAPI } from '../core/OpenAPI';
 import { request as __request } from '../core/request';
-
 export class WorkflowVersionService {
-
     /**
      * Get all versions of a workflow
      * List all versions of a Workflow.
@@ -46,7 +43,6 @@ export class WorkflowVersionService {
             },
         });
     }
-
     /**
      * Get a workflow version
      * Get a specific version of a workflow.
@@ -77,7 +73,6 @@ export class WorkflowVersionService {
             },
         });
     }
-
     /**
      * Update status of workflow version
      * Update the status of a workflow version.
@@ -111,7 +106,6 @@ export class WorkflowVersionService {
             },
         });
     }
-
     /**
      * Deprecate a workflow version
      * Deprecate a workflow version.
@@ -142,7 +136,6 @@ export class WorkflowVersionService {
             },
         });
     }
-
     /**
      * Fetch documentation for a workflow version
      * Get the documentation for a specific workflow version.
@@ -181,7 +174,6 @@ export class WorkflowVersionService {
             },
         });
     }
-
     /**
      * Upload icon for workflow version
      * Upload an icon for the workflow version and returns the new icon URL.
@@ -215,7 +207,6 @@ export class WorkflowVersionService {
             },
         });
     }
-
     /**
      * Delete icon of workflow version
      * Delete the icon of the workflow version.
@@ -245,5 +236,4 @@ export class WorkflowVersionService {
             },
         });
     }
-
 }
diff --git a/src/components/NavbarTop.vue b/src/components/NavbarTop.vue
index 42b2635..60447d3 100644
--- a/src/components/NavbarTop.vue
+++ b/src/components/NavbarTop.vue
@@ -119,11 +119,6 @@ watch(
                   >Available Workflows
                 </router-link>
               </li>
-              <li>
-                <router-link class="dropdown-item" :to="{ name: 'resources' }"
-                  >Available Resources
-                </router-link>
-              </li>
               <li>
                 <router-link
                   class="dropdown-item"
@@ -154,6 +149,40 @@ watch(
               </li>
             </ul>
           </li>
+          <li class="nav-item dropdown">
+            <a
+              class="nav-link dropdown-toggle"
+              :class="{ 'text-black': workflowActive }"
+              id="resourceDropdown"
+              href="#"
+              role="button"
+              data-bs-toggle="dropdown"
+              aria-expanded="false"
+              data-bs-auto-close="true"
+            >
+              Resources
+            </a>
+            <ul
+              class="dropdown-menu shadow m-0"
+              aria-labelledby="workflowDropdown"
+            >
+              <li>
+                <router-link class="dropdown-item" :to="{ name: 'resources' }"
+                  >Available Resources
+                </router-link>
+              </li>
+              <li v-if="store.resourceMaintainer || store.admin">
+                <hr class="dropdown-divider" />
+              </li>
+              <li v-if="store.resourceMaintainer || store.admin">
+                <router-link
+                  class="dropdown-item"
+                  :to="{ name: 'resource-maintainer' }"
+                  >My Resources
+                </router-link>
+              </li>
+            </ul>
+          </li>
         </ul>
       </div>
       <div class="dropdown" v-if="store.authenticated && store.user != null">
diff --git a/src/components/transitions/ResourceCard.vue b/src/components/resources/ResourceCard.vue
similarity index 97%
rename from src/components/transitions/ResourceCard.vue
rename to src/components/resources/ResourceCard.vue
index 2bcc585..f3c9140 100644
--- a/src/components/transitions/ResourceCard.vue
+++ b/src/components/resources/ResourceCard.vue
@@ -93,7 +93,7 @@ const resourceVersions = computed<ResourceVersionOut[]>(
                   }}
                 </p>
                 <div>
-                  Path on Cluster: <br />{{ resourceVersion.cluster_path }}
+                  Nextflow Access Path: <br />{{ resourceVersion.cluster_path }}
                 </div>
               </div>
             </div>
diff --git a/src/components/resources/createResourceModal.vue b/src/components/resources/createResourceModal.vue
new file mode 100644
index 0000000..27dc54c
--- /dev/null
+++ b/src/components/resources/createResourceModal.vue
@@ -0,0 +1,205 @@
+<script setup lang="ts">
+import { reactive, onMounted, ref } from "vue";
+import BootstrapModal from "@/components/modals/BootstrapModal.vue";
+import { Modal } from "bootstrap";
+import { useResourceStore } from "@/stores/resources";
+import type { ResourceIn } from "@/client/resource";
+
+const resourceRepository = useResourceStore();
+
+const resourceCreateForm = ref<HTMLFormElement | undefined>(undefined);
+const resourceNameElement = ref<HTMLInputElement | undefined>(undefined);
+
+const resource = reactive<ResourceIn>({
+  name: "",
+  description: "",
+  release: "",
+  source: "",
+});
+
+const formState = reactive<{
+  validated: boolean;
+  resourceNameTaken: boolean;
+  loading: boolean;
+}>({
+  validated: false,
+  resourceNameTaken: false,
+  loading: false,
+});
+
+const props = defineProps<{
+  modalID: string;
+}>();
+
+let createResourceModal: Modal | null = null;
+
+onMounted(() => {
+  createResourceModal = new Modal("#" + props.modalID);
+});
+
+function createResource() {
+  formState.validated = true;
+  // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+  formState.resourceNameTaken = false;
+  resource.description = resource.description.trim();
+  resource.name = resource.name.trim();
+  resourceNameElement.value?.setCustomValidity("");
+  if (resourceCreateForm.value?.checkValidity()) {
+    formState.loading = true;
+    resourceRepository
+      .createResource(resource)
+      .then(() => {
+        createResourceModal?.hide();
+        resource.name = "";
+        resource.description = "";
+        resource.source = "";
+        resource.release = "";
+        formState.resourceNameTaken = false;
+        formState.validated = false;
+      })
+      .catch((error) => {
+        if (
+          error.status === 400 &&
+          error.body["detail"] ===
+            `Resource with name '${resource.name}' already exists`
+        ) {
+          console.log(`Resource with name ${resource.name} already exists`);
+          formState.resourceNameTaken = true;
+          resourceNameElement.value?.setCustomValidity(
+            "Resource name is already taken",
+          );
+        }
+      })
+      .finally(() => {
+        formState.loading = false;
+      });
+  }
+}
+
+function modalClosed() {
+  formState.validated = false;
+  formState.resourceNameTaken = false;
+  resourceNameElement.value?.setCustomValidity("");
+}
+</script>
+
+<template>
+  <bootstrap-modal
+    :modalID="modalID"
+    :static-backdrop="true"
+    modal-label="Create Resource Modal"
+    v-on="{ 'hidden.bs.modal': modalClosed }"
+  >
+    <template v-slot:header> Create new Resource</template>
+    <template v-slot:body>
+      <form
+        id="resourceCreateForm"
+        :class="{ 'was-validated': formState.validated }"
+        ref="resourceCreateForm"
+      >
+        <div class="mb-3">
+          <label for="resourceNameInput" class="form-label"
+            >Resource Name</label
+          >
+          <div class="input-group">
+            <input
+              type="text"
+              class="form-control"
+              id="resourceNameInput"
+              required
+              minlength="3"
+              maxlength="32"
+              v-model="resource.name"
+              ref="resourceNameElement"
+            />
+            <div class="invalid-feedback">
+              <div v-if="formState.resourceNameTaken">
+                Resource name already taken.
+              </div>
+              <div>
+                Requirements
+                <ul>
+                  <li>At least 3 Characters long</li>
+                  <li>Unique in CloWM</li>
+                </ul>
+              </div>
+            </div>
+          </div>
+        </div>
+        <div class="mb-3">
+          <label for="resourceDescriptionInput" class="form-label">
+            Description
+          </label>
+          <div class="input-group">
+            <textarea
+              class="form-control"
+              id="resourceDescriptionInput"
+              required
+              rows="3"
+              minlength="16"
+              maxlength="256"
+              v-model="resource.description"
+              placeholder="Describe the purpose of the resource"
+            ></textarea>
+            <div class="invalid-feedback">
+              Requirements
+              <ul>
+                <li>At least 16 Characters long</li>
+              </ul>
+            </div>
+          </div>
+        </div>
+        <div class="mb-3">
+          <label for="resourceSourceInput" class="form-label"> Source </label>
+          <div class="input-group">
+            <input
+              class="form-control"
+              id="resourceSourceInput"
+              required
+              minlength="8"
+              maxlength="264"
+              v-model="resource.source"
+              placeholder="The source of the resource (e.g. a link)"
+            />
+          </div>
+        </div>
+        <div class="mb-3">
+          <label for="resourceReleaseInput" class="form-label"> Release </label>
+          <div class="input-group">
+            <input
+              class="form-control"
+              id="resourceReleaseInput"
+              required
+              minlength="3"
+              maxlength="32"
+              v-model="resource.release"
+              placeholder="The name of the release"
+            />
+          </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="resourceCreateForm"
+        class="btn btn-primary"
+        :disabled="formState.loading"
+        @click.prevent="createResource"
+      >
+        <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></style>
diff --git a/src/router/index.ts b/src/router/index.ts
index 4a35bb5..1154704 100644
--- a/src/router/index.ts
+++ b/src/router/index.ts
@@ -1,6 +1,9 @@
 import { createRouter, createWebHistory } from "vue-router";
 import DashboardView from "../views/DashboardView.vue";
 import LoginView from "../views/LoginView.vue";
+import { workflowRoutes } from "@/router/workflowRoutes";
+import { s3Routes } from "@/router/s3Routes";
+import { resourceRoutes } from "@/router/resourceRoutes";
 
 const router = createRouter({
   history: createWebHistory(import.meta.env.BASE_URL),
@@ -9,109 +12,7 @@ const router = createRouter({
       path: "/dashboard",
       name: "dashboard",
       component: DashboardView,
-      children: [
-        {
-          path: "object-storage/buckets",
-          name: "buckets",
-          component: () => import("../views/object-storage/BucketsView.vue"),
-          props: (route) => ({
-            bucketName: route.params.bucketName ?? undefined,
-          }),
-          children: [
-            {
-              path: ":bucketName/:subFolders*",
-              name: "bucket",
-              component: () => import("../views/object-storage/BucketView.vue"),
-              props: true,
-            },
-          ],
-        },
-        {
-          path: "object-storage/s3-keys",
-          name: "s3_keys",
-          component: () => import("../views/object-storage/S3KeysView.vue"),
-        },
-        {
-          path: "resources",
-          name: "resources",
-          component: () => import("../views/resources/ListResourcesView.vue"),
-        },
-        {
-          path: "workflow-executions",
-          name: "workflow-executions",
-          component: () =>
-            import("../views/workflows/ListWorkflowExecutionsView.vue"),
-        },
-        {
-          path: "workflows",
-          name: "workflows",
-          component: () => import("../views/workflows/ListWorkflowsView.vue"),
-        },
-        {
-          path: "developer/workflows",
-          name: "workflows-developer",
-          component: () => import("../views/workflows/MyWorkflowsView.vue"),
-          meta: {
-            requiresDeveloperRole: true,
-          },
-        },
-        {
-          path: "reviewer/workflows",
-          name: "workflows-reviewer",
-          component: () => import("../views/workflows/ReviewWorkflowsView.vue"),
-          meta: {
-            requiresReviewerRole: true,
-          },
-        },
-        {
-          path: "workflows/arbitrary",
-          name: "arbitrary-workflow",
-          component: () =>
-            import("../views/workflows/ArbitraryWorkflowView.vue"),
-          meta: {
-            requiresDeveloperRole: true,
-          },
-          props: (route) => ({
-            wid: route.query.wid,
-          }),
-        },
-        {
-          path: "workflows/:workflowId",
-          name: "workflow",
-          component: () => import("../views/workflows/WorkflowView.vue"),
-          props: (route) => ({
-            versionId: route.params.versionId ?? undefined,
-            workflowId: route.params.workflowId,
-            workflowModeId: route.query.workflowModeId ?? undefined,
-            developerView: route.query.developerView == "true" ?? false,
-          }),
-          children: [
-            {
-              path: "version/:versionId",
-              name: "workflow-version",
-              component: () =>
-                import("../views/workflows/WorkflowVersionView.vue"),
-              props: (route) => ({
-                versionId: route.params.versionId,
-                workflowId: route.params.workflowId,
-                activeTab: route.query.tab ?? "description",
-                workflowModeId: route.query.workflowModeId ?? undefined,
-              }),
-            },
-            {
-              path: "version/:versionId/start",
-              name: "workflow-start",
-              component: () =>
-                import("../views/workflows/StartWorkflowView.vue"),
-              props: (route) => ({
-                versionId: route.params.versionId,
-                workflowId: route.params.workflowId,
-                workflowModeId: route.query.workflowModeId ?? undefined,
-              }),
-            },
-          ],
-        },
-      ],
+      children: [...resourceRoutes, ...s3Routes, ...workflowRoutes],
     },
     {
       path: "/login",
diff --git a/src/router/resourceRoutes.ts b/src/router/resourceRoutes.ts
new file mode 100644
index 0000000..24b7db1
--- /dev/null
+++ b/src/router/resourceRoutes.ts
@@ -0,0 +1,14 @@
+import type { RouteRecordRaw } from "vue-router";
+
+export const resourceRoutes: RouteRecordRaw[] = [
+  {
+    path: "resources",
+    name: "resources",
+    component: () => import("../views/resources/ListResourcesView.vue"),
+  },
+  {
+    path: "developer/resources",
+    name: "resource-maintainer",
+    component: () => import("../views/resources/MyResourcesView.vue"),
+  },
+];
diff --git a/src/router/s3Routes.ts b/src/router/s3Routes.ts
new file mode 100644
index 0000000..30ff3e0
--- /dev/null
+++ b/src/router/s3Routes.ts
@@ -0,0 +1,25 @@
+import type { RouteRecordRaw } from "vue-router";
+
+export const s3Routes: RouteRecordRaw[] = [
+  {
+    path: "object-storage/buckets",
+    name: "buckets",
+    component: () => import("../views/object-storage/BucketsView.vue"),
+    props: (route) => ({
+      bucketName: route.params.bucketName ?? undefined,
+    }),
+    children: [
+      {
+        path: ":bucketName/:subFolders*",
+        name: "bucket",
+        component: () => import("../views/object-storage/BucketView.vue"),
+        props: true,
+      },
+    ],
+  },
+  {
+    path: "object-storage/s3-keys",
+    name: "s3_keys",
+    component: () => import("../views/object-storage/S3KeysView.vue"),
+  },
+];
diff --git a/src/router/workflowRoutes.ts b/src/router/workflowRoutes.ts
new file mode 100644
index 0000000..05aab0e
--- /dev/null
+++ b/src/router/workflowRoutes.ts
@@ -0,0 +1,76 @@
+import type { RouteRecordRaw } from "vue-router";
+
+export const workflowRoutes: RouteRecordRaw[] = [
+  {
+    path: "workflow-executions",
+    name: "workflow-executions",
+    component: () =>
+      import("../views/workflows/ListWorkflowExecutionsView.vue"),
+  },
+  {
+    path: "workflows",
+    name: "workflows",
+    component: () => import("../views/workflows/ListWorkflowsView.vue"),
+  },
+  {
+    path: "developer/workflows",
+    name: "workflows-developer",
+    component: () => import("../views/workflows/MyWorkflowsView.vue"),
+    meta: {
+      requiresDeveloperRole: true,
+    },
+  },
+  {
+    path: "reviewer/workflows",
+    name: "workflows-reviewer",
+    component: () => import("../views/workflows/ReviewWorkflowsView.vue"),
+    meta: {
+      requiresReviewerRole: true,
+    },
+  },
+  {
+    path: "workflows/arbitrary",
+    name: "arbitrary-workflow",
+    component: () => import("../views/workflows/ArbitraryWorkflowView.vue"),
+    meta: {
+      requiresDeveloperRole: true,
+    },
+    props: (route) => ({
+      wid: route.query.wid,
+    }),
+  },
+  {
+    path: "workflows/:workflowId",
+    name: "workflow",
+    component: () => import("../views/workflows/WorkflowView.vue"),
+    props: (route) => ({
+      versionId: route.params.versionId ?? undefined,
+      workflowId: route.params.workflowId,
+      workflowModeId: route.query.workflowModeId ?? undefined,
+      developerView: route.query.developerView == "true" ?? false,
+    }),
+    children: [
+      {
+        path: "version/:versionId",
+        name: "workflow-version",
+        component: () => import("../views/workflows/WorkflowVersionView.vue"),
+        props: (route) => ({
+          versionId: route.params.versionId,
+          workflowId: route.params.workflowId,
+          activeTab: route.query.tab ?? "description",
+          workflowModeId: route.query.workflowModeId ?? undefined,
+        }),
+      },
+      {
+        path: "version/:versionId/start",
+        name: "workflow-start",
+        component: () => import("../views/workflows/StartWorkflowView.vue"),
+        props: (route) => ({
+          versionId: route.params.versionId,
+          workflowId: route.params.workflowId,
+          workflowModeId: route.query.workflowModeId ?? undefined,
+        }),
+      },
+    ],
+  },
+];
diff --git a/src/stores/resources.ts b/src/stores/resources.ts
index 80f7581..ab5e18d 100644
--- a/src/stores/resources.ts
+++ b/src/stores/resources.ts
@@ -1,7 +1,8 @@
 import { defineStore } from "pinia";
-import type { ResourceOut } from "@/client/resource";
+import type { ResourceIn, ResourceOut } from "@/client/resource";
 import { ResourceService } from "@/client/resource";
 import { useAuthStore } from "@/stores/users";
+import { Status } from "@/client/resource";
 
 export const useResourceStore = defineStore({
   id: "resources",
@@ -17,6 +18,9 @@ export const useResourceStore = defineStore({
     resources(): ResourceOut[] {
       return Object.values(this.resourceMapping);
     },
+    ownResources(): ResourceOut[] {
+      return Object.values(this.ownResourceMapping);
+    },
   },
   actions: {
     fetchResources(onFinally?: () => void): Promise<ResourceOut[]> {
@@ -39,7 +43,10 @@ export const useResourceStore = defineStore({
       if (Object.keys(this.ownResourceMapping).length > 0) {
         onFinally?.();
       }
-      return ResourceService.resourceListResources(authStore.currentUID)
+      return ResourceService.resourceListResources(
+        authStore.currentUID,
+        Object.values(Status),
+      )
         .then((resources) => {
           const newMapping: Record<string, ResourceOut> = {};
           for (const resource of resources) {
@@ -50,5 +57,11 @@ export const useResourceStore = defineStore({
         })
         .finally(onFinally);
     },
+    async createResource(resource: ResourceIn): Promise<ResourceOut> {
+      const createdResource =
+        await ResourceService.resourceCreateResource(resource);
+      this.ownResourceMapping[createdResource.resource_id] = createdResource;
+      return createdResource;
+    },
   },
 });
diff --git a/src/stores/users.ts b/src/stores/users.ts
index d353f4c..4d7b524 100644
--- a/src/stores/users.ts
+++ b/src/stores/users.ts
@@ -79,6 +79,10 @@ export const useAuthStore = defineStore({
       state.user?.roles?.includes(RoleEnum.DEVELOPER) ??
       state.decodedToken?.roles.includes(RoleEnum.DEVELOPER) ??
       false,
+    resourceMaintainer: (state) =>
+      state.user?.roles?.includes(RoleEnum.DB_MAINTAINER) ??
+      state.decodedToken?.roles.includes(RoleEnum.DB_MAINTAINER) ??
+      false,
     admin: (state) =>
       state.user?.roles?.includes(RoleEnum.ADMINISTRATOR) ??
       state.decodedToken?.roles.includes(RoleEnum.ADMINISTRATOR) ??
@@ -106,6 +110,8 @@ export const useAuthStore = defineStore({
     },
     updateUser(user: User) {
       this.user = user;
+      this.userMapping[user.uid] = user.display_name;
+      localStorage.setItem(user.uid, user.display_name);
     },
     logout() {
       S3ProxyOpenAPI.TOKEN = undefined;
diff --git a/src/views/resources/ListResourcesView.vue b/src/views/resources/ListResourcesView.vue
index a460bf1..db883da 100644
--- a/src/views/resources/ListResourcesView.vue
+++ b/src/views/resources/ListResourcesView.vue
@@ -1,7 +1,7 @@
 <script setup lang="ts">
 import { computed, onMounted, reactive } from "vue";
 import { useResourceStore } from "@/stores/resources";
-import ResourceCard from "@/components/transitions/ResourceCard.vue";
+import ResourceCard from "@/components/resources/ResourceCard.vue";
 import CardTransitionGroup from "@/components/transitions/CardTransitionGroup.vue";
 import { useAuthStore } from "@/stores/users";
 import FontAwesomeIcon from "@/components/FontAwesomeIcon.vue";
diff --git a/src/views/resources/MyResourcesView.vue b/src/views/resources/MyResourcesView.vue
new file mode 100644
index 0000000..3b100fc
--- /dev/null
+++ b/src/views/resources/MyResourcesView.vue
@@ -0,0 +1,50 @@
+<script setup lang="ts">
+import { onMounted, reactive } from "vue";
+import { useResourceStore } from "@/stores/resources";
+import CardTransitionGroup from "@/components/transitions/CardTransitionGroup.vue";
+import ResourceCard from "@/components/resources/ResourceCard.vue";
+import CreateResourceModal from "@/components/resources/createResourceModal.vue";
+
+const resourceRepository = useResourceStore();
+
+const resourceState = reactive<{
+  loading: boolean;
+}>({
+  loading: true,
+});
+
+onMounted(() => {
+  resourceRepository.fetchOwnResources(() => {
+    resourceState.loading = false;
+  });
+});
+</script>
+
+<template>
+  <create-resource-modal modal-i-d="createResourceModal" />
+  <div
+    class="row m-2 border-bottom mb-4 justify-content-between align-items-center pb-2"
+  >
+    <h2 class="w-fit">My Resources</h2>
+    <button
+      class="btn btn-lg btn-primary w-fit"
+      data-bs-toggle="modal"
+      data-bs-target="#createResourceModal"
+    >
+      Create
+    </button>
+  </div>
+  <CardTransitionGroup
+    class="d-flex flex-wrap align-items-center justify-content-between"
+  >
+    <resource-card
+      v-for="resource in resourceRepository.ownResources"
+      :key="resource.resource_id"
+      :resource="resource"
+      :loading="false"
+      style="min-width: 48%"
+    />
+  </CardTransitionGroup>
+</template>
+
+<style scoped></style>
-- 
GitLab