diff --git a/.env.unix.example b/.env.unix.example
index 3ece8c28877959e1291dbcb8113f7f67bc45ad25..9152e53c7c1ff5b7761e36bad8a39a61ae44dcce 100644
--- a/.env.unix.example
+++ b/.env.unix.example
@@ -14,5 +14,5 @@ BROKER_PASSWORD=fda
 KEYCLOAK_ADMIN=fda
 KEYCLOAK_ADMIN_PASSWORD=fda
 BROKER_CONSUMERS=2
-WEBSITE=http://example.com
+WEBSITE=http://localhost
 GATEWAY_ENDPOINT=http://gateway-service:9095
\ No newline at end of file
diff --git a/docker-compose.yml b/docker-compose.yml
index ea9260df449a4a37fa2d999955ad313a0af156cd..d7fad131454f448ab9a2d19f26a7f3df3fbe3e3c 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -136,9 +136,8 @@ services:
     networks:
       core:
     ports:
-      - "9097:9097"
       - "8443:8443"
-      - "8080:8080"
+      - "8081:8080"
     env_file:
       - .env
     volumes:
@@ -362,8 +361,6 @@ services:
     networks:
       core:
       public:
-    ports:
-      - "3000:3000"
     env_file:
       - .env
     volumes:
@@ -375,3 +372,29 @@ services:
         condition: service_healthy
     logging:
       driver: json-file
+
+  fda-proxy:
+    restart: no
+    container_name: dbrepo-proxy
+    hostname: proxy
+    build: ./fda-proxy
+    image: fda-proxy
+    networks:
+      core:
+      public:
+    ports:
+      - "80:80"
+      - "8080:8080"
+      - "3000:3000"
+      - "443:443"
+    env_file:
+      - .env
+    depends_on:
+      fda-search-service:
+        condition: service_started
+      fda-gateway-service:
+        condition: service_healthy
+      fda-ui:
+        condition: service_started
+    logging:
+      driver: json-file
diff --git a/fda-authentication-service/dbrepo-realm.json b/fda-authentication-service/dbrepo-realm.json
index 4fbca0146ae43df33caa79539ab5578be27a9b1a..afe3f517a9fe65ffb192f5156087ecbf1e470156 100644
--- a/fda-authentication-service/dbrepo-realm.json
+++ b/fda-authentication-service/dbrepo-realm.json
@@ -4,7 +4,7 @@
   "notBefore" : 0,
   "defaultSignatureAlgorithm" : "RS256",
   "revokeRefreshToken" : false,
-  "refreshTokenMaxReuse" : 0,
+  "refreshTokenMaxReuse" : 1,
   "accessTokenLifespan" : 720,
   "accessTokenLifespanForImplicitFlow" : 900,
   "ssoSessionIdleTimeout" : 1800,
@@ -870,7 +870,7 @@
   "otpPolicyLookAheadWindow" : 1,
   "otpPolicyPeriod" : 30,
   "otpPolicyCodeReusable" : false,
-  "otpSupportedApplications" : [ "totpAppGoogleName", "totpAppFreeOTPName", "totpAppMicrosoftAuthenticatorName" ],
+  "otpSupportedApplications" : [ "totpAppFreeOTPName", "totpAppGoogleName", "totpAppMicrosoftAuthenticatorName" ],
   "webAuthnPolicyRpEntityName" : "keycloak",
   "webAuthnPolicySignatureAlgorithms" : [ "ES256" ],
   "webAuthnPolicyRpId" : "",
@@ -1820,6 +1820,23 @@
       "config" : {
         "allow-default-scopes" : [ "true" ]
       }
+    }, {
+      "id" : "1849e52a-b8c9-44a8-af3d-ee19376a1ed1",
+      "name" : "Trusted Hosts",
+      "providerId" : "trusted-hosts",
+      "subType" : "anonymous",
+      "subComponents" : { },
+      "config" : {
+        "host-sending-registration-request-must-match" : [ "true" ],
+        "client-uris-must-match" : [ "true" ]
+      }
+    }, {
+      "id" : "f565cb47-3bcf-4078-8f94-eb4179c375b8",
+      "name" : "Full Scope Disabled",
+      "providerId" : "scope",
+      "subType" : "anonymous",
+      "subComponents" : { },
+      "config" : { }
     }, {
       "id" : "0efa669d-1017-4b4a-82e1-c2eaf72de2c9",
       "name" : "Allowed Client Scopes",
@@ -1837,60 +1854,25 @@
       "subComponents" : { },
       "config" : { }
     }, {
-      "id" : "3ab11d74-5e76-408a-b85a-26bf8950f979",
+      "id" : "104ec5a9-025b-4c44-8ac0-82d22887ca3e",
       "name" : "Allowed Protocol Mapper Types",
       "providerId" : "allowed-protocol-mappers",
-      "subType" : "anonymous",
-      "subComponents" : { },
-      "config" : {
-        "allowed-protocol-mapper-types" : [ "oidc-usermodel-attribute-mapper", "saml-user-attribute-mapper", "saml-user-property-mapper", "oidc-address-mapper", "oidc-full-name-mapper", "saml-role-list-mapper", "oidc-sha256-pairwise-sub-mapper", "oidc-usermodel-property-mapper" ]
-      }
-    }, {
-      "id" : "1849e52a-b8c9-44a8-af3d-ee19376a1ed1",
-      "name" : "Trusted Hosts",
-      "providerId" : "trusted-hosts",
-      "subType" : "anonymous",
+      "subType" : "authenticated",
       "subComponents" : { },
       "config" : {
-        "host-sending-registration-request-must-match" : [ "true" ],
-        "client-uris-must-match" : [ "true" ]
+        "allowed-protocol-mapper-types" : [ "oidc-sha256-pairwise-sub-mapper", "oidc-usermodel-attribute-mapper", "oidc-usermodel-property-mapper", "saml-role-list-mapper", "oidc-address-mapper", "saml-user-attribute-mapper", "oidc-full-name-mapper", "saml-user-property-mapper" ]
       }
     }, {
-      "id" : "f565cb47-3bcf-4078-8f94-eb4179c375b8",
-      "name" : "Full Scope Disabled",
-      "providerId" : "scope",
-      "subType" : "anonymous",
-      "subComponents" : { },
-      "config" : { }
-    }, {
-      "id" : "104ec5a9-025b-4c44-8ac0-82d22887ca3e",
+      "id" : "3ab11d74-5e76-408a-b85a-26bf8950f979",
       "name" : "Allowed Protocol Mapper Types",
       "providerId" : "allowed-protocol-mappers",
-      "subType" : "authenticated",
+      "subType" : "anonymous",
       "subComponents" : { },
       "config" : {
-        "allowed-protocol-mapper-types" : [ "saml-role-list-mapper", "oidc-sha256-pairwise-sub-mapper", "oidc-usermodel-attribute-mapper", "saml-user-attribute-mapper", "oidc-full-name-mapper", "oidc-address-mapper", "oidc-usermodel-property-mapper", "saml-user-property-mapper" ]
+        "allowed-protocol-mapper-types" : [ "saml-user-property-mapper", "oidc-usermodel-attribute-mapper", "oidc-usermodel-property-mapper", "saml-user-attribute-mapper", "oidc-full-name-mapper", "oidc-sha256-pairwise-sub-mapper", "oidc-address-mapper", "saml-role-list-mapper" ]
       }
     } ],
-    "org.keycloak.userprofile.UserProfileProvider" : [ {
-      "id" : "2abcde8e-479c-4b3f-85ca-08687ff4b0cf",
-      "providerId" : "declarative-user-profile",
-      "subComponents" : { },
-      "config" : { }
-    } ],
     "org.keycloak.keys.KeyProvider" : [ {
-      "id" : "2f53ccf3-37b0-4d34-83e7-ed497499ee51",
-      "name" : "rsa-enc-generated",
-      "providerId" : "rsa-enc-generated",
-      "subComponents" : { },
-      "config" : {
-        "privateKey" : [ "MIIEowIBAAKCAQEA3b1tNLfcjFLUw9UShVDNf+ZD8sQqb4YBaIXcSJTX/zDQUPiCp176BBGI3s4VplDArnOW+LumozmKogeoHEnGEIDW8ovgK5uMU9tSA2p0qqGBUMOdR8YATTIfCJe7qGiiuGa3WZy3sQLM70SuRzx02YU8gvUcvl2Js4KyqAziOUX/w3Wa59H9jjGNUXYyqaPWJp73eHzbVYWySzyLG22mVlcUtBx5siL5T2/Xu0p9z4l7/bapwwmOVi1ZrcHjbEAwdGEiSMGI/uWqAF+r1BRpmJLR7HNXcL3eK4/56VYLaiwSejfyYeRFMITEn/UxGYhcXZ5xMUUCG0TxjBhLYpTBuwIDAQABAoIBAA4dwebcxkrH99Poa8+WkiE7JgaS9sahx9OBI2xwJANoIU2TpzGuNLQZ76uLgB+rPWZTD9Xm5a1iJjwOyQ9/937TzPCk91D0tpgcusRikb8jx/6TGB9acL4kBjYUVCCHr3BA2G75MKKGtJ2OMvAbCQSosZj+r2VDwYFEPUkV2jheE5JHSBkwyIRrus3JCwu8gu5fyCg9z8ljcxJxI5HIsi4v8Z21aCw/cLj7h5cMt44wCjQz4rOfYNBEFeHDtlfR1QtWKgjm4ZHHJbKrzf9b2kQXclziceEbSM0tYbROEXKi+s0Zc+z3HEG89vv0vfN400clmzzIAijKY6gg3pPRWdECgYEA+lnWYbSlXDMNYx6RBXm1RnlMUYIm4oy4/9ljgnoGJ6WCn3SjFkgaDtiKfGIG1BSB85r04pAPANgcWHf5tWDnq0ARvBVG0BX2bKd++7B3D4d3CRYKCwm88SslJXv9dfHVhq4+zViFPiUWwT20A72jCuUCvL88y5fh/YBecfdh+jECgYEA4r5RD0NB9dMaeg5/jk/GEHIo4Z9KLc6FrSoOFo2xFkPOy1sgDpDOiNtypuWvniO7k7Ose3DS3hlfTMsKzIW/CgQJ20+Y4cvBWDaOsRxfjj7w3d+jH5OSJdKKSzTrgLKc9ZhlRzVXy0J0hipIA6HG5kdVdLXmh85ITmf1CbJhE6sCgYBjPVeBNbXTHZ2x6/z62aslO5IoQVqetb/kE82hfDOSZcao5Ph9Lam+ttH2ynkAevykj4mBgi+gWwqpey2uW7KaLPSaxShj9kDQA3mP1fzsV/u0y1rB02Nlin/YIxVvOqU1FT9p8SwoXVVu1sHUNck62VtDbN9xqUx5S/ikXrclEQKBgQCoTssOwEcK+Vty9KYcdfy4onTUHZBLdjxl8Iyqkxy7QTQUYRznkvesQPDXEDGO+kk3dyx2KKZt9Hl4IFNww2quPZcvcuMx4DQxjbXXpA8OIIxcta95NepLJwA+mRai3nKCH1A2TlNP7pFeMa5o+8IPly3Ix2lKr4Wepa4PN5i1pwKBgCZ1QP6XAOERl9NznNmU0rXVcvYNP4PIIfQWfvGsldZ4QKkmjjAGiS0/oYqdWs+UDRZyCRChaVjDXO9fk0PEG5OGKAj9nyiYCT/M8xtJ3UeP5ffZZvJ/vnye3QdDIo1e38ZzsWwJHmLYw7fRqY9W5Vxo0Vsy22U3CJY70KTxVdTy" ],
-        "keyUse" : [ "ENC" ],
-        "certificate" : [ "MIICmzCCAYMCBgGG3GWycDANBgkqhkiG9w0BAQsFADARMQ8wDQYDVQQDDAZkYnJlcG8wHhcNMjMwMzEzMTkxMzE3WhcNMzMwMzEzMTkxNDU3WjARMQ8wDQYDVQQDDAZkYnJlcG8wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDdvW00t9yMUtTD1RKFUM1/5kPyxCpvhgFohdxIlNf/MNBQ+IKnXvoEEYjezhWmUMCuc5b4u6ajOYqiB6gcScYQgNbyi+Arm4xT21IDanSqoYFQw51HxgBNMh8Il7uoaKK4ZrdZnLexAszvRK5HPHTZhTyC9Ry+XYmzgrKoDOI5Rf/DdZrn0f2OMY1RdjKpo9Ymnvd4fNtVhbJLPIsbbaZWVxS0HHmyIvlPb9e7Sn3PiXv9tqnDCY5WLVmtweNsQDB0YSJIwYj+5aoAX6vUFGmYktHsc1dwvd4rj/npVgtqLBJ6N/Jh5EUwhMSf9TEZiFxdnnExRQIbRPGMGEtilMG7AgMBAAEwDQYJKoZIhvcNAQELBQADggEBAK3kQ1VkQrzvSWvmXmazmNoA1ZiPzRDs1XhGUWxgsxzgPylr3dGBuqQbKvgnLUBQLSqlJHpI4fZflHswu1qrvVZYtekPcGef4WhcKAu2i1RwxrKa6RJQ1tRbrLuVYCzPv5p/DWgltWVn88aoLnqQn0SK/0PB/o4a4Cm7Kq2ZzCr1dACBr06LvOHsc7249OySmbG4HH+pLK6jVURhZ9VaObqAHe2FJBVVoIzURbdiRRURqumrIvbnpeaU1aFyg6ED5wTnXvmMPmVPt9F79mcB33bASO5wyu00X8t1hyN2Show2l2vxLACGUzVkTQt15s7uDLKE7qLmKSR3EuSGXWv3wA=" ],
-        "priority" : [ "100" ],
-        "algorithm" : [ "RSA-OAEP" ]
-      }
-    }, {
       "id" : "28ca0b6d-b2e2-4785-b04b-2391e6344e30",
       "name" : "aes-generated",
       "providerId" : "aes-generated",
@@ -1911,6 +1893,18 @@
         "priority" : [ "100" ],
         "algorithm" : [ "HS256" ]
       }
+    }, {
+      "id" : "2f53ccf3-37b0-4d34-83e7-ed497499ee51",
+      "name" : "rsa-enc-generated",
+      "providerId" : "rsa-enc-generated",
+      "subComponents" : { },
+      "config" : {
+        "privateKey" : [ "MIIEowIBAAKCAQEA3b1tNLfcjFLUw9UShVDNf+ZD8sQqb4YBaIXcSJTX/zDQUPiCp176BBGI3s4VplDArnOW+LumozmKogeoHEnGEIDW8ovgK5uMU9tSA2p0qqGBUMOdR8YATTIfCJe7qGiiuGa3WZy3sQLM70SuRzx02YU8gvUcvl2Js4KyqAziOUX/w3Wa59H9jjGNUXYyqaPWJp73eHzbVYWySzyLG22mVlcUtBx5siL5T2/Xu0p9z4l7/bapwwmOVi1ZrcHjbEAwdGEiSMGI/uWqAF+r1BRpmJLR7HNXcL3eK4/56VYLaiwSejfyYeRFMITEn/UxGYhcXZ5xMUUCG0TxjBhLYpTBuwIDAQABAoIBAA4dwebcxkrH99Poa8+WkiE7JgaS9sahx9OBI2xwJANoIU2TpzGuNLQZ76uLgB+rPWZTD9Xm5a1iJjwOyQ9/937TzPCk91D0tpgcusRikb8jx/6TGB9acL4kBjYUVCCHr3BA2G75MKKGtJ2OMvAbCQSosZj+r2VDwYFEPUkV2jheE5JHSBkwyIRrus3JCwu8gu5fyCg9z8ljcxJxI5HIsi4v8Z21aCw/cLj7h5cMt44wCjQz4rOfYNBEFeHDtlfR1QtWKgjm4ZHHJbKrzf9b2kQXclziceEbSM0tYbROEXKi+s0Zc+z3HEG89vv0vfN400clmzzIAijKY6gg3pPRWdECgYEA+lnWYbSlXDMNYx6RBXm1RnlMUYIm4oy4/9ljgnoGJ6WCn3SjFkgaDtiKfGIG1BSB85r04pAPANgcWHf5tWDnq0ARvBVG0BX2bKd++7B3D4d3CRYKCwm88SslJXv9dfHVhq4+zViFPiUWwT20A72jCuUCvL88y5fh/YBecfdh+jECgYEA4r5RD0NB9dMaeg5/jk/GEHIo4Z9KLc6FrSoOFo2xFkPOy1sgDpDOiNtypuWvniO7k7Ose3DS3hlfTMsKzIW/CgQJ20+Y4cvBWDaOsRxfjj7w3d+jH5OSJdKKSzTrgLKc9ZhlRzVXy0J0hipIA6HG5kdVdLXmh85ITmf1CbJhE6sCgYBjPVeBNbXTHZ2x6/z62aslO5IoQVqetb/kE82hfDOSZcao5Ph9Lam+ttH2ynkAevykj4mBgi+gWwqpey2uW7KaLPSaxShj9kDQA3mP1fzsV/u0y1rB02Nlin/YIxVvOqU1FT9p8SwoXVVu1sHUNck62VtDbN9xqUx5S/ikXrclEQKBgQCoTssOwEcK+Vty9KYcdfy4onTUHZBLdjxl8Iyqkxy7QTQUYRznkvesQPDXEDGO+kk3dyx2KKZt9Hl4IFNww2quPZcvcuMx4DQxjbXXpA8OIIxcta95NepLJwA+mRai3nKCH1A2TlNP7pFeMa5o+8IPly3Ix2lKr4Wepa4PN5i1pwKBgCZ1QP6XAOERl9NznNmU0rXVcvYNP4PIIfQWfvGsldZ4QKkmjjAGiS0/oYqdWs+UDRZyCRChaVjDXO9fk0PEG5OGKAj9nyiYCT/M8xtJ3UeP5ffZZvJ/vnye3QdDIo1e38ZzsWwJHmLYw7fRqY9W5Vxo0Vsy22U3CJY70KTxVdTy" ],
+        "keyUse" : [ "ENC" ],
+        "certificate" : [ "MIICmzCCAYMCBgGG3GWycDANBgkqhkiG9w0BAQsFADARMQ8wDQYDVQQDDAZkYnJlcG8wHhcNMjMwMzEzMTkxMzE3WhcNMzMwMzEzMTkxNDU3WjARMQ8wDQYDVQQDDAZkYnJlcG8wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDdvW00t9yMUtTD1RKFUM1/5kPyxCpvhgFohdxIlNf/MNBQ+IKnXvoEEYjezhWmUMCuc5b4u6ajOYqiB6gcScYQgNbyi+Arm4xT21IDanSqoYFQw51HxgBNMh8Il7uoaKK4ZrdZnLexAszvRK5HPHTZhTyC9Ry+XYmzgrKoDOI5Rf/DdZrn0f2OMY1RdjKpo9Ymnvd4fNtVhbJLPIsbbaZWVxS0HHmyIvlPb9e7Sn3PiXv9tqnDCY5WLVmtweNsQDB0YSJIwYj+5aoAX6vUFGmYktHsc1dwvd4rj/npVgtqLBJ6N/Jh5EUwhMSf9TEZiFxdnnExRQIbRPGMGEtilMG7AgMBAAEwDQYJKoZIhvcNAQELBQADggEBAK3kQ1VkQrzvSWvmXmazmNoA1ZiPzRDs1XhGUWxgsxzgPylr3dGBuqQbKvgnLUBQLSqlJHpI4fZflHswu1qrvVZYtekPcGef4WhcKAu2i1RwxrKa6RJQ1tRbrLuVYCzPv5p/DWgltWVn88aoLnqQn0SK/0PB/o4a4Cm7Kq2ZzCr1dACBr06LvOHsc7249OySmbG4HH+pLK6jVURhZ9VaObqAHe2FJBVVoIzURbdiRRURqumrIvbnpeaU1aFyg6ED5wTnXvmMPmVPt9F79mcB33bASO5wyu00X8t1hyN2Show2l2vxLACGUzVkTQt15s7uDLKE7qLmKSR3EuSGXWv3wA=" ],
+        "priority" : [ "100" ],
+        "algorithm" : [ "RSA-OAEP" ]
+      }
     }, {
       "id" : "2293ff99-3c6d-46d1-8635-5e679d5b134a",
       "name" : "rsa-generated",
@@ -1927,7 +1921,7 @@
   "internationalizationEnabled" : false,
   "supportedLocales" : [ ],
   "authenticationFlows" : [ {
-    "id" : "9b2ffbe1-91b5-4815-b2c6-fdb8d5cf522e",
+    "id" : "f687eb46-5fae-49f4-a38c-e34263c44f45",
     "alias" : "Account verification options",
     "description" : "Method with which to verity the existing account",
     "providerId" : "basic-flow",
@@ -1949,7 +1943,7 @@
       "userSetupAllowed" : false
     } ]
   }, {
-    "id" : "d48e99bc-ce6f-4474-b1f1-2b87c578522d",
+    "id" : "f97dde75-21ae-4e8e-be0b-1e74f876a1dd",
     "alias" : "Authentication Options",
     "description" : "Authentication options.",
     "providerId" : "basic-flow",
@@ -1978,7 +1972,7 @@
       "userSetupAllowed" : false
     } ]
   }, {
-    "id" : "61b23580-7996-49c4-8370-77bb1532c818",
+    "id" : "3e54a8dd-917e-424d-adb8-78e0e90fe18f",
     "alias" : "Browser - Conditional OTP",
     "description" : "Flow to determine if the OTP is required for the authentication",
     "providerId" : "basic-flow",
@@ -2000,7 +1994,7 @@
       "userSetupAllowed" : false
     } ]
   }, {
-    "id" : "598a4244-04b4-4a8d-9c99-1e1a41f1243b",
+    "id" : "f22e8af1-8e3c-4399-a8e1-3a0a285e7d65",
     "alias" : "Direct Grant - Conditional OTP",
     "description" : "Flow to determine if the OTP is required for the authentication",
     "providerId" : "basic-flow",
@@ -2022,7 +2016,7 @@
       "userSetupAllowed" : false
     } ]
   }, {
-    "id" : "e7ee21a3-baf9-4259-b4c2-7ca8742d0521",
+    "id" : "6889ac8b-1fb5-4504-a480-34bdf6d6e041",
     "alias" : "First broker login - Conditional OTP",
     "description" : "Flow to determine if the OTP is required for the authentication",
     "providerId" : "basic-flow",
@@ -2044,7 +2038,7 @@
       "userSetupAllowed" : false
     } ]
   }, {
-    "id" : "718cf803-48b1-4a96-83b5-bad0ad92cdbb",
+    "id" : "33eab85c-b74a-4e81-8624-fa068545c1f1",
     "alias" : "Handle Existing Account",
     "description" : "Handle what to do if there is existing account with same email/username like authenticated identity provider",
     "providerId" : "basic-flow",
@@ -2066,7 +2060,7 @@
       "userSetupAllowed" : false
     } ]
   }, {
-    "id" : "2dae43c5-af72-4a1c-b315-798892e76982",
+    "id" : "ae3d35c8-14e7-4723-86df-d0898fe5bec5",
     "alias" : "Reset - Conditional OTP",
     "description" : "Flow to determine if the OTP should be reset or not. Set to REQUIRED to force.",
     "providerId" : "basic-flow",
@@ -2088,7 +2082,7 @@
       "userSetupAllowed" : false
     } ]
   }, {
-    "id" : "ca2e30ea-389c-403a-89de-950bbc488ad4",
+    "id" : "8d60cb94-57a7-4150-979d-626358119e36",
     "alias" : "User creation or linking",
     "description" : "Flow for the existing/non-existing user alternatives",
     "providerId" : "basic-flow",
@@ -2111,7 +2105,7 @@
       "userSetupAllowed" : false
     } ]
   }, {
-    "id" : "b11e2f51-ef59-4393-be70-064ed4e1321b",
+    "id" : "ff6c20e7-b379-4055-99b9-7a2e4024e138",
     "alias" : "Verify Existing Account by Re-authentication",
     "description" : "Reauthentication of existing account",
     "providerId" : "basic-flow",
@@ -2133,7 +2127,7 @@
       "userSetupAllowed" : false
     } ]
   }, {
-    "id" : "9a39c4b1-ba3c-403c-9620-b93e5a9da467",
+    "id" : "373b8617-2016-4df5-bf1b-75b8a67ec8b2",
     "alias" : "browser",
     "description" : "browser based authentication",
     "providerId" : "basic-flow",
@@ -2169,7 +2163,7 @@
       "userSetupAllowed" : false
     } ]
   }, {
-    "id" : "6c1d6c8e-e593-40d6-89c8-7b6044790717",
+    "id" : "61a55658-9750-44ce-8edf-51030643b9eb",
     "alias" : "clients",
     "description" : "Base authentication for clients",
     "providerId" : "client-flow",
@@ -2205,7 +2199,7 @@
       "userSetupAllowed" : false
     } ]
   }, {
-    "id" : "f650a4f6-e0b8-47da-8416-b805d7cb8535",
+    "id" : "be7636a4-4efa-4f88-894f-fbefa8750f31",
     "alias" : "direct grant",
     "description" : "OpenID Connect Resource Owner Grant",
     "providerId" : "basic-flow",
@@ -2234,7 +2228,7 @@
       "userSetupAllowed" : false
     } ]
   }, {
-    "id" : "363ceb8b-0902-4f97-9006-93d4e6fa3d9a",
+    "id" : "7fa9637a-ab90-42b4-95da-ed93c3480ba4",
     "alias" : "docker auth",
     "description" : "Used by Docker clients to authenticate against the IDP",
     "providerId" : "basic-flow",
@@ -2249,7 +2243,7 @@
       "userSetupAllowed" : false
     } ]
   }, {
-    "id" : "41584462-4e61-45d3-bf42-cf1f19266804",
+    "id" : "48f53c92-8d4a-4e48-9bb1-de1daf756332",
     "alias" : "first broker login",
     "description" : "Actions taken after first broker login with identity provider account, which is not yet linked to any Keycloak account",
     "providerId" : "basic-flow",
@@ -2272,7 +2266,7 @@
       "userSetupAllowed" : false
     } ]
   }, {
-    "id" : "e723d622-3bf1-4202-bf51-69de9548ec20",
+    "id" : "34bcc343-9321-41d3-aa33-eb653181812d",
     "alias" : "forms",
     "description" : "Username, password, otp and other auth forms.",
     "providerId" : "basic-flow",
@@ -2294,7 +2288,7 @@
       "userSetupAllowed" : false
     } ]
   }, {
-    "id" : "e6f26e36-d5cd-47e7-be78-7b9d21187a42",
+    "id" : "ff3d99ae-9bbd-46aa-9c29-0c55d3c5c9c4",
     "alias" : "http challenge",
     "description" : "An authentication flow based on challenge-response HTTP Authentication Schemes",
     "providerId" : "basic-flow",
@@ -2316,7 +2310,7 @@
       "userSetupAllowed" : false
     } ]
   }, {
-    "id" : "e548e0c5-596a-467f-a23b-00b4ddbf68d3",
+    "id" : "103b0a30-7ad6-41ec-827f-bacdb155496f",
     "alias" : "registration",
     "description" : "registration flow",
     "providerId" : "basic-flow",
@@ -2332,7 +2326,7 @@
       "userSetupAllowed" : false
     } ]
   }, {
-    "id" : "0cdcba23-b485-416e-873b-f1695646bef8",
+    "id" : "f080e6b0-a34e-49b6-bc17-c4777e147535",
     "alias" : "registration form",
     "description" : "registration form",
     "providerId" : "form-flow",
@@ -2368,7 +2362,7 @@
       "userSetupAllowed" : false
     } ]
   }, {
-    "id" : "ed2d6f0c-4414-49e4-bda1-217cb40e168f",
+    "id" : "cb302fa5-c65a-48d9-94ea-1412ac567065",
     "alias" : "reset credentials",
     "description" : "Reset credentials for a user if they forgot their password or something",
     "providerId" : "basic-flow",
@@ -2404,7 +2398,7 @@
       "userSetupAllowed" : false
     } ]
   }, {
-    "id" : "a5d09fd7-e988-485b-a5e8-bb9a54e34c42",
+    "id" : "b281b498-55a2-4909-9138-bc9bde251925",
     "alias" : "saml ecp",
     "description" : "SAML ECP Profile Authentication Flow",
     "providerId" : "basic-flow",
@@ -2420,13 +2414,13 @@
     } ]
   } ],
   "authenticatorConfig" : [ {
-    "id" : "da63f903-8393-4686-8187-6ca865a79448",
+    "id" : "78adce28-a91c-4e60-9816-d521ac667de1",
     "alias" : "create unique user config",
     "config" : {
       "require.password.update.after.registration" : "false"
     }
   }, {
-    "id" : "96a14ace-debb-42f0-8dff-701891d6048a",
+    "id" : "1474c494-141c-448d-a5ec-45c4e43a5939",
     "alias" : "review profile config",
     "config" : {
       "update.profile.on.first.login" : "missing"
diff --git a/fda-gateway-service/rest-service/src/main/resources/application-local.yml b/fda-gateway-service/rest-service/src/main/resources/application-local.yml
index f1c31c526a7c7ea57f54caf56dffba75a06ffa15..320e14ae2ff24ced3b20791aeb12d31c93d46524 100644
--- a/fda-gateway-service/rest-service/src/main/resources/application-local.yml
+++ b/fda-gateway-service/rest-service/src/main/resources/application-local.yml
@@ -22,10 +22,6 @@ spring:
     name: gateway-service
   cloud:
     loadbalancer.ribbon.enabled: false
-    gateway:
-      httpclient:
-        ssl:
-          useInsecureTrustManager: true
 management.endpoints.web.exposure.include: health,info,prometheus,gateway
 server:
   port: 9095
diff --git a/fda-identifier-service/Dockerfile b/fda-identifier-service/Dockerfile
index 43768d4a7c6e293ed7e823df846c50e758e4e0d7..e0bab808145952fe5fdaeaa3acc4714bb152791a 100644
--- a/fda-identifier-service/Dockerfile
+++ b/fda-identifier-service/Dockerfile
@@ -26,7 +26,7 @@ ENV METADATA_DB=fda
 ENV METADATA_USERNAME=root
 ENV METADATA_PASSWORD=dbrepo
 ENV GATEWAY_ENDPOINT=http://gateway-service:9095
-ENV WEBSITE=http://localhost:3000
+ENV WEBSITE=http://localhost
 ENV LOG_LEVEL=debug
 ENV DBREPO_CLIENT_SECRET=client-secret
 ENV CLIENT_ID=dbrepo-client
diff --git a/fda-metadata-db/api/src/main/java/at/tuwien/api/user/UserAttributeDto.java b/fda-metadata-db/api/src/main/java/at/tuwien/api/user/UserAttributeDto.java
index ee3e1252788c06df6d94857eaf76bc84bc78c175..c5f7b7b0b34bc1d3036ed393e7ef1e979591c7b0 100644
--- a/fda-metadata-db/api/src/main/java/at/tuwien/api/user/UserAttributeDto.java
+++ b/fda-metadata-db/api/src/main/java/at/tuwien/api/user/UserAttributeDto.java
@@ -1,7 +1,6 @@
 package at.tuwien.api.user;
 
 import com.fasterxml.jackson.annotation.JsonIgnore;
-import com.fasterxml.jackson.annotation.JsonProperty;
 import io.swagger.v3.oas.annotations.media.Schema;
 import lombok.*;
 
@@ -21,7 +20,6 @@ public class UserAttributeDto {
 
     @NotNull
     @JsonIgnore
-    @JsonProperty("user_id")
     @Schema(example = "1ffc7b0e-9aeb-4e8b-b8f1-68f3936155b4")
     private String userId;
 
diff --git a/fda-metadata-db/entities/src/main/java/at/tuwien/entities/user/Credential.java b/fda-metadata-db/entities/src/main/java/at/tuwien/entities/user/Credential.java
index a189fb25d023b90a606762c3e19f97a78476fa8a..f3da481c38b003df917e0ef0003159c9a6fe5066 100644
--- a/fda-metadata-db/entities/src/main/java/at/tuwien/entities/user/Credential.java
+++ b/fda-metadata-db/entities/src/main/java/at/tuwien/entities/user/Credential.java
@@ -42,6 +42,7 @@ public class Credential {
     @Column(nullable = false)
     private Integer priority;
 
+    @ToString.Exclude
     @ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
     @JoinColumns({
             @JoinColumn(name = "user_id", referencedColumnName = "id", insertable = false, updatable = false)
diff --git a/fda-metadata-db/entities/src/main/java/at/tuwien/entities/user/User.java b/fda-metadata-db/entities/src/main/java/at/tuwien/entities/user/User.java
index 75db0aa7076363314e89b5bbb522568464772c87..5aec9968769b5c22b491a87c6e4dbfd212c29923 100644
--- a/fda-metadata-db/entities/src/main/java/at/tuwien/entities/user/User.java
+++ b/fda-metadata-db/entities/src/main/java/at/tuwien/entities/user/User.java
@@ -69,6 +69,7 @@ public class User {
     private List<UserAttribute> attributes;
 
     @Column(nullable = false)
+    @ToString.Exclude
     @OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, mappedBy = "user")
     private List<Credential> credentials;
 
diff --git a/fda-metadata-db/entities/src/main/java/at/tuwien/entities/user/UserAttribute.java b/fda-metadata-db/entities/src/main/java/at/tuwien/entities/user/UserAttribute.java
index c0bf71be22e6c2ef7fd0b208a9a1ed5bb3e8aa4c..fbcfc82cf5512f55ca20cec5bac0089ffd4a1d36 100644
--- a/fda-metadata-db/entities/src/main/java/at/tuwien/entities/user/UserAttribute.java
+++ b/fda-metadata-db/entities/src/main/java/at/tuwien/entities/user/UserAttribute.java
@@ -19,14 +19,13 @@ import javax.persistence.*;
 public class UserAttribute {
 
     @Id
-    @JsonIgnore
     @EqualsAndHashCode.Include
     @GeneratedValue(generator = "attribute-uuid")
     @GenericGenerator(name = "attribute-uuid", strategy = "org.hibernate.id.UUIDGenerator")
     @Column(name = "ID", nullable = false, columnDefinition = "VARCHAR(36)")
     private String id;
 
-    @JsonIgnore
+    @ToString.Exclude
     @Column(name = "USER_ID", nullable = false)
     private String userId;
 
diff --git a/fda-proxy/Dockerfile b/fda-proxy/Dockerfile
new file mode 100644
index 0000000000000000000000000000000000000000..6086e614d19011a1df5854c3af5881fa7350a237
--- /dev/null
+++ b/fda-proxy/Dockerfile
@@ -0,0 +1,4 @@
+FROM nginx:alpine AS runtime
+MAINTAINER Martin Weise <martin.weise@tuwien.ac.at
+
+COPY ./dbrepo.conf /etc/nginx/conf.d/default.conf
diff --git a/fda-proxy/dbrepo.conf b/fda-proxy/dbrepo.conf
new file mode 100644
index 0000000000000000000000000000000000000000..ea6efa7759ba799c81ee96c144ed8d2e35d618e3
--- /dev/null
+++ b/fda-proxy/dbrepo.conf
@@ -0,0 +1,42 @@
+server {
+    listen 80 default_server;
+    server_name _;
+
+    location /api {
+        proxy_set_header        Host $host;
+        proxy_set_header        X-Real-IP $remote_addr;
+        proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;
+        proxy_set_header        X-Forwarded-Proto $scheme;
+        proxy_pass              http://gateway-service:9095;
+        proxy_read_timeout      90;
+    }
+
+    location /pid {
+        rewrite /pid/(.*) /api/pid/$1 break;
+        proxy_set_header        Host $host;
+        proxy_set_header        X-Real-IP $remote_addr;
+        proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;
+        proxy_set_header        X-Forwarded-Proto $scheme;
+        proxy_pass              http://gateway-service:9095;
+        proxy_read_timeout      90;
+    }
+
+    location /retrieve {
+        rewrite /retrieve/(.*) /$1 break;
+        proxy_set_header        Host $host;
+        proxy_set_header        X-Real-IP $remote_addr;
+        proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;
+        proxy_set_header        X-Forwarded-Proto $scheme;
+        proxy_pass              http://search-service:9200;
+        proxy_read_timeout      90;
+    }
+
+    location / {
+        proxy_set_header        Host $host;
+        proxy_set_header        X-Real-IP $remote_addr;
+        proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;
+        proxy_set_header        X-Forwarded-Proto $scheme;
+        proxy_pass              http://ui:3000/;
+        proxy_read_timeout      90;
+    }
+}
diff --git a/fda-query-service/services/src/main/java/at/tuwien/service/impl/RabbitMqServiceImpl.java b/fda-query-service/services/src/main/java/at/tuwien/service/impl/RabbitMqServiceImpl.java
index 89bcb5425ec77f72cf971366b3bd70ec2e80c7dd..e814476f1d0032afd82b0fc8d6bf48df1581f788 100644
--- a/fda-query-service/services/src/main/java/at/tuwien/service/impl/RabbitMqServiceImpl.java
+++ b/fda-query-service/services/src/main/java/at/tuwien/service/impl/RabbitMqServiceImpl.java
@@ -69,16 +69,11 @@ public class RabbitMqServiceImpl implements MessageQueueService {
         for (Table table : tables) {
             final long consumerCount = consumers.stream().filter(c -> c.getQueue().getName().equals(table.getQueueName())).count();
             if (consumerCount >= amqpConfig.getAmqpConsumers()) {
-                log.trace("listener table with name {} already has {} consumers (max. {})", table.getName(),
-                        consumerCount, amqpConfig.getAmqpConsumers());
                 continue;
             }
-            log.debug("table with id {} has {} consumers, but needs {} in total", table.getId(), consumerCount,
-                    amqpConfig.getAmqpConsumers());
             for (long i = consumerCount; i < amqpConfig.getAmqpConsumers(); i++) {
                 createConsumer(table.getQueueName(), table.getDatabase().getContainer().getId(),
                         table.getDatabase().getId(), table.getId());
-                log.trace("creating consumer #{}", i);
             }
         }
     }
diff --git a/fda-ui/Dockerfile b/fda-ui/Dockerfile
index 048398eebaa2a7b2b4b887c685df515a1adbaae5..729a01636095da65efede0bbee586c81025d7db4 100644
--- a/fda-ui/Dockerfile
+++ b/fda-ui/Dockerfile
@@ -8,7 +8,7 @@ ARG TAG=1.2
 
 ENV NODE_ENV=production
 ENV HOST=0.0.0.0
-ENV API=http://gateway-service:9095
+ENV API=http://:8080
 
 WORKDIR /app
 
diff --git a/fda-ui/api/analyse.service.js b/fda-ui/api/analyse.service.js
new file mode 100644
index 0000000000000000000000000000000000000000..fec2f309121356cd8015fd1c28b7ea6669c4b692
--- /dev/null
+++ b/fda-ui/api/analyse.service.js
@@ -0,0 +1,23 @@
+import Vue from 'vue'
+import api from '@/api'
+
+class AnalyseService {
+  determineDataTypes (filepath) {
+    return new Promise((resolve, reject) => {
+      api.post('/api/analyse/determinedt', { filepath }, { headers: { Accept: 'application/json' } })
+        .then((response) => {
+          const analysis = response.data
+          console.debug('response analysis', analysis)
+          resolve(analysis)
+        })
+        .catch((error) => {
+          const { code, message } = error
+          console.error('Failed to load analysis', error)
+          Vue.$toast.error(`[${code}] Failed to load analysis: ${message}`)
+          reject(error)
+        })
+    })
+  }
+}
+
+export default new AnalyseService()
diff --git a/fda-ui/api/analyse/index.js b/fda-ui/api/analyse/index.js
deleted file mode 100644
index 66ad332a56dfc7b58104f77c5c8fdd206e4f25a7..0000000000000000000000000000000000000000
--- a/fda-ui/api/analyse/index.js
+++ /dev/null
@@ -1,12 +0,0 @@
-const axios = require('axios/dist/browser/axios.cjs')
-
-export function determineDataTypes (token, filepath) {
-  const payload = {
-    filepath
-  }
-  return axios.post('/api/analyse/determinedt', payload, {
-    headers: {
-      Authorization: `Bearer ${token}`
-    }
-  })
-}
diff --git a/fda-ui/api/authentication.service.js b/fda-ui/api/authentication.service.js
index 33dcadd1f25e9dfe138247b46eee372f25767296..df567c8add375a689d7cf14ba5cdc7d74c574d6e 100644
--- a/fda-ui/api/authentication.service.js
+++ b/fda-ui/api/authentication.service.js
@@ -3,7 +3,7 @@ import store from '@/store'
 import qs from 'qs'
 import UserMapper from '@/api/user.mapper'
 import axios from 'axios'
-import { clientSecret } from '@/config'
+import { api as endpoint, clientSecret } from '@/config'
 
 /**
  * Service class for interaction with Authentication Service in the back end.
@@ -25,7 +25,7 @@ class AuthenticationService {
       password,
       grant_type: 'password',
       client_secret: clientSecret,
-      scope: 'openid profile roles attributes'
+      scope: 'roles'
     }
     if (!username) {
       throw new Error('parameter username is empty')
@@ -57,29 +57,42 @@ class AuthenticationService {
 
   _authenticate (payload) {
     return new Promise((resolve, reject) => {
-      axios.post('/api/auth/realms/dbrepo/protocol/openid-connect/token', qs.stringify(payload), {
+      const instance = axios.create({
+        timeout: 10000,
+        params: {},
+        baseURL: endpoint,
         headers: {
           'Content-Type': 'application/x-www-form-urlencoded'
         }
-      }).then((response) => {
-        const authentication = response.data
-        // eslint-disable-next-line camelcase
-        const { access_token, refresh_token } = authentication
-        store().commit('SET_TOKEN', access_token)
-        store().commit('SET_REFRESH_TOKEN', refresh_token)
-        store().commit('SET_ROLES', UserMapper.tokenToRoles(access_token))
-        resolve(authentication)
-      }).catch((error) => {
-        console.error('Failed to authenticate', error)
-        const { code, message, response } = error
-        const { status } = response
-        if (status === 401) {
-          Vue.$toast.error('Invalid username-password combination.')
-        } else {
-          Vue.$toast.error(`[${code}] Failed to authenticate: ${message}`)
-        }
-        reject(error)
       })
+      instance.post('/api/auth/realms/dbrepo/protocol/openid-connect/token', qs.stringify(payload))
+        .then((response) => {
+          const authentication = response.data
+          // eslint-disable-next-line camelcase
+          const { access_token, refresh_token } = authentication
+          store().commit('SET_TOKEN', access_token)
+          store().commit('SET_REFRESH_TOKEN', refresh_token)
+          store().commit('SET_ROLES', UserMapper.tokenToRoles(access_token))
+          resolve(authentication)
+        }).catch((error) => {
+          console.error('Failed to authenticate', error)
+          const { code, message, response } = error
+          const { status, data } = response
+          if (status === 401) {
+            Vue.$toast.error('Invalid username-password combination.')
+          } else if (data?.error === 'invalid_grant') {
+            store().commit('SET_TOKEN', null)
+            store().commit('SET_REFRESH_TOKEN', null)
+            store().commit('SET_ROLES', [])
+            store().commit('SET_USER', null)
+            this.$vuetify.theme.dark = false
+            Vue.$toast.warning('Authentication expired.')
+            this.$router.push('/login')
+          } else {
+            Vue.$toast.error(`[${code}] Failed to authenticate: ${message}`)
+          }
+          reject(error)
+        })
     })
   }
 }
diff --git a/fda-ui/api/index.js b/fda-ui/api/index.js
index d26eeaab7be40a4f0b7450a251ba12f46fdb6e50..53c4143ccfe36402902d4ab9086c4f04a4194793 100644
--- a/fda-ui/api/index.js
+++ b/fda-ui/api/index.js
@@ -1,12 +1,13 @@
-import https from 'https'
 import axios from 'axios'
-
-const httpsAgent = new https.Agent({ rejectUnauthorized: false })
+import { api as endpoint } from '@/config'
 
 const instance = axios.create({
   timeout: 10000,
   params: {},
-  httpsAgent
+  baseURL: endpoint,
+  headers: {
+    'Access-Control-Allow-Origin': '*'
+  }
 })
 
 export default instance
diff --git a/fda-ui/api/middleware.service.js b/fda-ui/api/middleware.service.js
new file mode 100644
index 0000000000000000000000000000000000000000..165013f4963fb85a5630467c994c71d901545821
--- /dev/null
+++ b/fda-ui/api/middleware.service.js
@@ -0,0 +1,25 @@
+import Vue from 'vue'
+import axios from 'axios'
+
+class MiddlewareService {
+  upload (file) {
+    return new Promise((resolve, reject) => {
+      const data = new FormData()
+      data.append('file', file)
+      axios.post('/server-middleware/upload', data, { headers: { 'Content-Type': 'multipart/form-data' } })
+        .then((response) => {
+          const file = response.data
+          console.debug('response file', file)
+          resolve(file)
+        })
+        .catch((error) => {
+          const { code, message } = error
+          console.error('Failed to create database', error)
+          Vue.$toast.error(`[${code}] Failed to create database: ${message}`)
+          reject(error)
+        })
+    })
+  }
+}
+
+export default new MiddlewareService()
diff --git a/fda-ui/api/query.service.js b/fda-ui/api/query.service.js
index 8fc66756bacb5c95f7be93600f68c5438ea86d18..d74601bd533e3b457209282819c0ae09586a3b7c 100644
--- a/fda-ui/api/query.service.js
+++ b/fda-ui/api/query.service.js
@@ -52,13 +52,89 @@ class QueryService {
     })
   }
 
-  export (id, databaseId, queryId) {
+  importCsv (id, databaseId, tableId, data) {
+    return new Promise((resolve, reject) => {
+      api.post(`/api/container/${id}/database/${databaseId}/table/${tableId}/data/import`, data, { headers: { Accept: 'application/json' } })
+        .then((response) => {
+          const table = response.data
+          console.debug('response table', table)
+          resolve(table)
+        })
+        .catch((error) => {
+          const { code, message } = error
+          console.error('Failed to import csv to table', error)
+          Vue.$toast.error(`[${code}] Failed to import csv to table: ${message}`)
+          reject(error)
+        })
+    })
+  }
+
+  insertTuple (id, databaseId, tableId, data) {
+    return new Promise((resolve, reject) => {
+      api.post(`/api/container/${id}/database/${databaseId}/table/${tableId}/data`, data, { headers: { Accept: 'text/csv' } })
+        .then((response) => {
+          const tuple = response.data
+          console.debug('response insert tuple', tuple)
+          resolve(tuple)
+        })
+        .catch((error) => {
+          const { code, message, response } = error
+          const { status } = response
+          if (status === 423) {
+            console.error('Database failed to accept tuple', error)
+            Vue.$toast.error(`Database failed to accept tuple: ${message}`)
+          } else {
+            console.error('Failed to insert tuple', error)
+            Vue.$toast.error(`[${code}] Failed to insert tuple: ${message}`)
+          }
+          reject(error)
+        })
+    })
+  }
+
+  updateTuple (id, databaseId, tableId, data) {
+    return new Promise((resolve, reject) => {
+      api.put(`/api/container/${id}/database/${databaseId}/table/${tableId}/data`, data, { headers: { Accept: 'text/csv' } })
+        .then((response) => {
+          const tuple = response.data
+          console.debug('response update tuple', tuple)
+          resolve(tuple)
+        })
+        .catch((error) => {
+          const { code, message, response } = error
+          const { status } = response
+          if (status === 423) {
+            console.error('Database failed to accept tuple', error)
+            Vue.$toast.error(`Database failed to accept tuple: ${message}`)
+          } else {
+            console.error('Failed to update tuple', error)
+            Vue.$toast.error(`[${code}] Failed to update tuple: ${message}`)
+          }
+          reject(error)
+        })
+    })
+  }
+
+  exportSubset (id, databaseId, queryId) {
     return new Promise((resolve, reject) => {
       api.put(`/api/container/${id}/database/${databaseId}/query/${queryId}/export`, {}, { headers: { Accept: 'text/csv' } })
         .then((response) => {
-          const query = response.data
-          console.debug('response export', query)
-          resolve(query)
+          resolve(response.data)
+        })
+        .catch((error) => {
+          const { code, message } = error
+          console.error('Failed to export query', error)
+          Vue.$toast.error(`[${code}] Failed to export query: ${message}`)
+          reject(error)
+        })
+    })
+  }
+
+  exportMetadata (id, mime) {
+    return new Promise((resolve, reject) => {
+      api.get(`/api/pid/${id}`, { headers: { Accept: mime } })
+        .then((response) => {
+          resolve(response.data)
         })
         .catch((error) => {
           const { code, message } = error
diff --git a/fda-ui/api/table.service.js b/fda-ui/api/table.service.js
index 36a932200a39debe8fe7ab9d135089a2d8348f9d..3b0b55379c93b10102ab3d117d3e106b607b5796 100644
--- a/fda-ui/api/table.service.js
+++ b/fda-ui/api/table.service.js
@@ -57,23 +57,6 @@ class TableService {
         })
     })
   }
-
-  importCsv (id, databaseId, tableId, data) {
-    return new Promise((resolve, reject) => {
-      api.post(`/api/container/${id}/database/${databaseId}/table/${tableId}/import`, data, { headers: { Accept: 'application/json' } })
-        .then((response) => {
-          const table = response.data
-          console.debug('response table', table)
-          resolve(table)
-        })
-        .catch((error) => {
-          const { code, message } = error
-          console.error('Failed to import csv to table', error)
-          Vue.$toast.error(`[${code}] Failed to import csv to table: ${message}`)
-          reject(error)
-        })
-    })
-  }
 }
 
 export default new TableService()
diff --git a/fda-ui/components/DBToolbar.vue b/fda-ui/components/DBToolbar.vue
index 026f7dce95417d0e4ba4a9d7addc3d4e5db350f3..973d45682b5a6ff83de0b69257da52ede2a7ae6e 100644
--- a/fda-ui/components/DBToolbar.vue
+++ b/fda-ui/components/DBToolbar.vue
@@ -29,16 +29,16 @@
       <v-spacer />
       <v-toolbar-title>
         <v-btn v-if="canImportCsv" class="mr-2 mb-1" :to="`/container/${$route.params.container_id}/database/${$route.params.database_id}/table/import`">
-          <v-icon left>mdi-cloud-upload</v-icon> Import CSV
+          Import .csv
         </v-btn>
         <v-btn v-if="canCreateSubset" color="secondary" class="mb-1 white--text" :to="`/container/${$route.params.container_id}/database/${$route.params.database_id}/query/create`">
-          <v-icon left>mdi-wrench</v-icon> Create Subset
+          Create Subset
         </v-btn>
         <v-btn v-if="canCreateView" color="secondary" class="ml-2 mr-2 mb-1 white--text" :to="`/container/${$route.params.container_id}/database/${$route.params.database_id}/view/create`">
-          <v-icon left>mdi-view-carousel-outline</v-icon> Create View
+          Create View
         </v-btn>
         <v-btn v-if="canCreateTable" color="primary" class="mb-1" :to="`/container/${$route.params.container_id}/database/${$route.params.database_id}/table/create`">
-          <v-icon left>mdi-table-large-plus</v-icon> Create Table
+          Create Table
         </v-btn>
       </v-toolbar-title>
       <template v-slot:extension>
diff --git a/fda-ui/components/DatabaseList.vue b/fda-ui/components/DatabaseList.vue
index 7dab9d6bddd8e3b203ed650cd8840320b7666d90..8fe1e8a0de97aa7c8b0755b71a4af4bcf1f39942 100644
--- a/fda-ui/components/DatabaseList.vue
+++ b/fda-ui/components/DatabaseList.vue
@@ -7,7 +7,7 @@
       :to="link(container)"
       flat
       tile>
-      <v-divider class="mx-4" />
+      <v-divider v-if="!$vuetify.theme.dark" class="mx-4" />
       <v-card-title v-if="!hasDatabase(container)" v-text="container.name" />
       <v-card-title v-if="hasDatabase(container)">
         <a :href="`/container/${container.id}/database/${container.database.id}`">{{ container.name }}</a>
diff --git a/fda-ui/components/TableSchema.vue b/fda-ui/components/TableSchema.vue
index 2042009818d6417217b0e2ef4b5ffe30857565ae..82593a90b19456a39f6d6f2a25f8ae39c0f9f69d 100644
--- a/fda-ui/components/TableSchema.vue
+++ b/fda-ui/components/TableSchema.vue
@@ -3,7 +3,7 @@
     <v-alert
       v-if="needsSequence"
       border="left"
-      color="amber lighten-4 black--text">
+      color="info">
       We create a column named <code>id</code> with a auto-increasing sequence starting at 1. Please specify a column with primary key if you don't want this behavior.
     </v-alert>
     <v-form ref="form" v-model="valid">
@@ -82,7 +82,7 @@
         </v-row>
       </div>
       <div>
-        <v-btn x-small :loading="loading" @click="addColumn()">
+        <v-btn x-small @click="addColumn()">
           Add Column
         </v-btn>
       </div>
@@ -90,7 +90,7 @@
         <v-btn v-if="back" class="mt-10 mr-2 mb-1" @click="stepBack()">
           Back
         </v-btn>
-        <v-btn color="primary" :loading="loading" :disabled="!valid" class="mt-10 mb-1" @click="submit()">
+        <v-btn color="primary" :loading="localLoading" :disabled="!valid" class="mt-10 mb-1" @click="submit">
           Continue
         </v-btn>
       </div>
@@ -118,11 +118,17 @@ export default {
       default () {
         return false
       }
+    },
+    loading: {
+      type: Boolean,
+      default () {
+        return false
+      }
     }
   },
   data () {
     return {
-      loading: false,
+      localLoading: false,
       dateFormats: [],
       valid: true,
       finished: false,
@@ -151,7 +157,13 @@ export default {
       return this.columns.filter(c => c.primary_key).length === 0
     }
   },
+  watch: {
+    loading () {
+      this.localLoading = this.loading
+    }
+  },
   mounted () {
+    this.localLoading = this.loading
     this.loadContainer()
       .then(() => this.loadImage())
   },
@@ -165,7 +177,7 @@ export default {
     async loadContainer () {
       const getUrl = `/api/container/${this.$route.params.container_id}`
       try {
-        this.loading = true
+        this.localLoading = true
         const res = await this.$axios.get(getUrl)
         this.container = res.data
         console.debug('retrieve container', this.container)
@@ -173,12 +185,12 @@ export default {
         this.error = true
         console.error('retrieve image date formats failed', err)
       }
-      this.loading = false
+      this.localLoading = false
     },
     async loadImage () {
       const getUrl = `/api/image/${this.container.image.id}`
       try {
-        this.loading = true
+        this.localLoading = true
         const res = await this.$axios.get(getUrl)
         this.dateFormats = res.data.date_formats
         console.debug('retrieve image date formats', this.dateFormats)
@@ -186,10 +198,11 @@ export default {
         this.error = true
         console.error('retrieve image date formats failed', err)
       }
-      this.loading = false
+      this.localLoading = false
     },
     submit () {
       this.finished = true
+      this.localLoading = true
       this.$emit('close', { success: true })
     },
     setOthers (column) {
diff --git a/fda-ui/components/TableToolbar.vue b/fda-ui/components/TableToolbar.vue
index 1469c3c3fbe22ce3143f25b79e8ea3ef786c4788..6203f5164214d5556d98cf28b8d97c93f1808dd6 100644
--- a/fda-ui/components/TableToolbar.vue
+++ b/fda-ui/components/TableToolbar.vue
@@ -12,22 +12,22 @@
       <v-spacer />
       <v-toolbar-title>
         <v-btn v-if="canAddTuple" class="mr-2 mb-1" @click="addTuple">
-          <v-icon left>mdi-plus</v-icon> Add
+          Add
         </v-btn>
         <v-btn v-if="canEditTuple" color="warning" class="mr-2 mb-1 black--text" @click="editTuple">
-          <v-icon left>mdi-pencil</v-icon> Edit
+          Edit
         </v-btn>
         <v-btn v-if="canDeleteTuple" color="error" class="mr-2 mb-1" :loading="loadingDelete" @click="deleteItems">
-          <v-icon left>mdi-delete</v-icon> Delete<span v-if="selection.length > 1">&nbsp;{{ selection.length }}</span>
+          Delete <span v-if="selection.length > 1">&nbsp;{{ selection.length }}</span>
         </v-btn>
         <v-btn v-if="canExecuteQuery" class="mb-1" :to="`/container/${$route.params.container_id}/database/${$route.params.database_id}/query/create?tid=${$route.params.table_id}`" color="secondary">
-          <v-icon left>mdi-wrench</v-icon> Create Subset
+          Create Subset
         </v-btn>
         <v-btn v-if="canCreateView" class="ml-2 mb-1" :to="`/container/${$route.params.container_id}/database/${$route.params.database_id}/view/create?tid=${$route.params.table_id}`" color="secondary">
-          <v-icon left>mdi-view-carousel</v-icon> Create View
+          Create View
         </v-btn>
         <v-btn v-if="canImportCsv" class="ml-2 mb-1" :to="`/container/${$route.params.container_id}/database/${$route.params.database_id}/table/${$route.params.table_id}/import`">
-          <v-icon left>mdi-cloud-upload</v-icon> Import csv
+          Import csv
         </v-btn>
       </v-toolbar-title>
     </v-toolbar>
@@ -35,7 +35,7 @@
       <v-tab :to="`/container/${$route.params.container_id}/database/${$route.params.database_id}/table/${$route.params.table_id}/info`">
         Info
       </v-tab>
-      <v-tab v-if="canReadData" :to="`/container/${$route.params.container_id}/database/${$route.params.database_id}/table/${$route.params.table_id}/data`">
+      <v-tab v-if="canViewTableData" :to="`/container/${$route.params.container_id}/database/${$route.params.database_id}/table/${$route.params.table_id}/data`">
         Data
       </v-tab>
       <v-tab :to="`/container/${$route.params.container_id}/database/${$route.params.database_id}/table/${$route.params.table_id}/schema`">
@@ -138,17 +138,18 @@ export default {
       }
       return this.roles.includes('create-database-view')
     },
-    canReadData () {
+    canViewTableData () {
+      /* view when database is public or when private: 1) view-table-data role present 2) access is at least read */
       if (!this.database) {
         return false
       }
       if (this.database.is_public) {
         return true
       }
-      if (!this.roles) {
+      if (!this.roles || !this.roles.includes('view-table-data') || !this.access) {
         return false
       }
-      return this.roles.includes('view-table-data')
+      return this.access.type === 'read' || this.access.type === 'write_own' || this.access.type === 'write_all'
     },
     canImportCsv () {
       if (!this.roles) {
diff --git a/fda-ui/components/UserToolbar.vue b/fda-ui/components/UserToolbar.vue
index 5eb2113386e259091babc67dbfdfff9c2a1e08eb..f8e7b086ca8200d40e44bdf70e58b4db4df90bd5 100644
--- a/fda-ui/components/UserToolbar.vue
+++ b/fda-ui/components/UserToolbar.vue
@@ -12,9 +12,6 @@
       <v-tab to="/user/authentication">
         Authentication
       </v-tab>
-      <v-tab v-if="false" to="/user/developer">
-        Developer
-      </v-tab>
     </v-tabs>
   </div>
 </template>
diff --git a/fda-ui/components/dialogs/CreateDB.vue b/fda-ui/components/dialogs/CreateDB.vue
index b2877b02809394dfec9831d4d500fa76f442a3f1..e73cd6f336e9b332c0c2c127d5c3394bfb6cdd19 100644
--- a/fda-ui/components/dialogs/CreateDB.vue
+++ b/fda-ui/components/dialogs/CreateDB.vue
@@ -2,33 +2,35 @@
   <div>
     <v-form ref="form" v-model="valid" autocomplete="off" @submit.prevent="submit">
       <v-card>
-        <v-card-title>
-          Create Database
-        </v-card-title>
+        <v-card-title>Create Database</v-card-title>
+        <v-card-subtitle>Choose an expressive database name and select a database engine.</v-card-subtitle>
         <v-card-text>
-          <v-alert
-            border="left"
-            color="info">
-            Choose an expressive database name and select a database engine.
-          </v-alert>
-          <v-text-field
-            id="database"
-            v-model="createContainerDto.name"
-            name="database"
-            label="Name *"
-            autofocus
-            :rules="[v => notEmpty(v) || $t('Required')]"
-            required />
-          <v-select
-            id="engine"
-            v-model="engine"
-            name="engine"
-            label="Engine *"
-            :items="engines"
-            :item-text="item => `${item.repository}:${item.tag}`"
-            :rules="[v => !!v || $t('Required')]"
-            return-object
-            required />
+          <v-row dense>
+            <v-col>
+              <v-text-field
+                id="database"
+                v-model="createContainerDto.name"
+                name="database"
+                label="Name *"
+                autofocus
+                :rules="[v => notEmpty(v) || $t('Required')]"
+                required />
+            </v-col>
+          </v-row>
+          <v-row dense>
+            <v-col>
+              <v-select
+                id="engine"
+                v-model="engine"
+                name="engine"
+                label="Engine *"
+                :items="engines"
+                :item-text="item => `${item.repository}:${item.tag}`"
+                :rules="[v => !!v || $t('Required')]"
+                return-object
+                required />
+            </v-col>
+          </v-row>
         </v-card-text>
         <v-card-actions>
           <v-spacer />
diff --git a/fda-ui/components/dialogs/EditAccess.vue b/fda-ui/components/dialogs/EditAccess.vue
index 5d063ca56bd86f2687072453368dd5499a91077f..4f30ef9d85c74d9efbd52f6181a00840e999f9d7 100644
--- a/fda-ui/components/dialogs/EditAccess.vue
+++ b/fda-ui/components/dialogs/EditAccess.vue
@@ -4,22 +4,26 @@
       <v-card>
         <v-progress-linear v-if="loading" :color="loadingColor" :indeterminate="!error" />
         <v-card-title v-text="title" />
+        <v-card-subtitle v-if="subtitle" v-text="subtitle" />
         <v-card-text>
-          <v-alert
-            v-if="modify.type && modify.type !== 'revoke'"
-            border="left"
-            color="warning">
-            <strong>Dangerous operation:</strong> you are giving this user access to <strong>{{ explanation }}</strong> in your database
-          </v-alert>
-          <v-alert
-            v-if="modify.type && modify.type === 'revoke'"
-            border="left"
-            color="error">
-            <strong>Dangerous operation:</strong> you are <strong>revoking</strong> all access for this user to your database
-          </v-alert>
+          <div v-if="!isModification">
+            <v-alert
+              v-if="modify.type && modify.type !== 'revoke'"
+              border="left"
+              color="warning">
+              <strong>Dangerous operation:</strong> you are giving this user access to <strong>{{ explanation }}</strong> in your database
+            </v-alert>
+            <v-alert
+              v-if="modify.type && modify.type === 'revoke'"
+              border="left"
+              color="error">
+              <strong>Dangerous operation:</strong> you are <strong>revoking</strong> all access for this user to your database
+            </v-alert>
+          </div>
           <v-row>
             <v-col>
               <v-autocomplete
+                v-if="!isModification"
                 v-model="modify.username"
                 :items="eligibleUsers"
                 :loading="loadingUsers"
@@ -30,7 +34,6 @@
                 hide-details
                 item-text="username"
                 item-value="username"
-                :disabled="isModification"
                 single-line
                 label="Username" />
             </v-col>
@@ -66,8 +69,6 @@
         </v-card-actions>
       </v-card>
     </v-form>
-    <pre>{{ eligibleUsers }}</pre>
-    <pre>{{ modify.username }}</pre>
   </div>
 </template>
 
@@ -125,7 +126,10 @@ export default {
       return this.$store.state.database
     },
     title () {
-      return (!this.isModification ? 'Give' : 'Modify') + ' database access' + (!this.isModification ? '' : ` of ${this.username}`)
+      return (!this.isModification ? 'Give' : 'Modify') + ' database access'
+    },
+    subtitle () {
+      return (this.isModification ? `User with username ${this.username}` : false)
     },
     accessTypes () {
       if (!this.isModification) {
@@ -135,10 +139,6 @@ export default {
       return this.types
     },
     eligibleUsers () {
-      if (this.accessType) {
-        /* this is a modification, list only the edited user as eligible */
-        return [{ username: this.username, id: '00000' }]
-      }
       return this.users.filter(u => !this.database.accesses.map(a => a.user.id).includes(u.id))
     },
     buttonColor () {
@@ -272,7 +272,6 @@ export default {
       } else {
         this.modify.type = this.accessType
       }
-      this.$refs.form.reset()
     }
   }
 }
diff --git a/fda-ui/components/dialogs/EditTuple.vue b/fda-ui/components/dialogs/EditTuple.vue
index a3bf26d5f60d32e26954e764d98a33d233e92716..c8407c457c417db1bc7fc73407378ba7f850d62c 100644
--- a/fda-ui/components/dialogs/EditTuple.vue
+++ b/fda-ui/components/dialogs/EditTuple.vue
@@ -98,7 +98,7 @@
           <v-btn
             v-if="edit"
             id="updateTuple"
-            class="mb-2"
+            class="mb-2 ml-3 mr-2"
             :disabled="!valid"
             color="primary"
             type="submit"
@@ -112,6 +112,8 @@
 </template>
 
 <script>
+import QueryService from '@/api/query.service'
+
 export default {
   props: {
     tuple: {
@@ -145,7 +147,7 @@ export default {
       return this.$store.state.token
     },
     title () {
-      return (this.edit ? 'Edit' : 'Add') + ' tuple'
+      return (this.edit ? 'Edit' : 'Add') + ' Tuple'
     }
   },
   watch: {
@@ -185,7 +187,7 @@ export default {
     validateTimestamp (val) {
       return /^[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}$/.test(val)
     },
-    async updateTuple () {
+    updateTuple () {
       const constraints = {}
       this.columns
         .filter(c => c.is_primary_key)
@@ -196,46 +198,29 @@ export default {
         data: this.localTuple,
         keys: constraints
       }
-      try {
-        await this.$axios.put(`/api/container/${this.$route.params.container_id}/database/${this.$route.params.database_id}/table/${this.$route.params.table_id}/data`, data, {
-          headers: { Authorization: `Bearer ${this.token}` }
+      QueryService.updateTuple(this.$route.params.container_id, this.$route.params.database_id, this.$route.params.table_id, data)
+        .then(() => {
+          this.$toast.success('Successfully updated tuple!')
+          this.$emit('close', { success: true })
         })
-        console.info('update result')
-        this.$toast.success('Successfully updated tuple!')
-        this.$emit('close', { success: true })
-      } catch (error) {
-        console.error('Failed to update tuple', error)
-        const { message } = error.response.data
-        this.$toast.error('Failed to update tuple: ' + message)
-      }
     },
-    async addTuple () {
+    addTuple () {
       const constraints = {}
       this.columns
         .filter(c => c.is_primary_key)
         .forEach((c) => {
           constraints[c.internal_name] = this.localTuple[c.internal_name]
         })
-      try {
-        const res = await this.$axios.post(`/api/container/${this.$route.params.container_id}/database/${this.$route.params.database_id}/table/${this.$route.params.table_id}/data`, {
-          data: this.tuple
-        }, {
-          headers: { Authorization: `Bearer ${this.token}` }
-        })
-        console.info('add result', res.data)
-        this.$toast.success('Successfully added tuple!')
-        this.$emit('close', { success: true })
-      } catch (error) {
-        console.error('Failed to add tuple', error)
-        const { message, status } = error.response.data
-        if (status === 423) {
-          console.error('Database failed to accept tuple', message)
-          this.$toast.error(`Database failed to accept tuple: ${message}`)
-        } else {
-          console.error('Failed to add tuple', message)
-          this.$toast.error(`${message}`)
+      this.columns.forEach((column) => {
+        if (!(column.internal_name in this.localTuple)) {
+          this.localTuple[column.internal_name] = null
         }
-      }
+      })
+      QueryService.insertTuple(this.$route.params.container_id, this.$route.params.database_id, this.$route.params.table_id, { data: this.localTuple })
+        .then(() => {
+          this.$toast.success('Successfully added tuple!')
+          this.$emit('close', { success: true })
+        })
     }
   }
 }
diff --git a/fda-ui/config.js b/fda-ui/config.js
index bff062a685fa2d01f6692136b798ae069b467dd3..cb738d6280ada1d091e30afab9486c8d6ae36f63 100644
--- a/fda-ui/config.js
+++ b/fda-ui/config.js
@@ -1,7 +1,7 @@
 const config = {}
 
-config.api = process.env.API || 'http://localhost:3000/api'
-config.search = process.env.SEARCH || 'http://localhost:9200'
+config.api = process.env.API || 'http://localhost'
+config.search = process.env.SEARCH || 'http://localhost/retrieve'
 config.sandbox = process.env.SANDBOX || false
 config.title = process.env.TITLE || 'Database Repository'
 config.icon = process.env.ICON || '/favicon.ico'
diff --git a/fda-ui/layouts/default.vue b/fda-ui/layouts/default.vue
index e7f52db13a801355b2c8dfdd9f623e3c097117ad..ce9934fec660d00f75a1b6fcc762e1fea25e37d8 100644
--- a/fda-ui/layouts/default.vue
+++ b/fda-ui/layouts/default.vue
@@ -214,7 +214,7 @@ export default {
       handler (id, oldId) {
         if (id !== oldId) {
           this.loadDatabase()
-          // this.loadAccess()
+          this.loadAccess()
         }
       },
       deep: true,
diff --git a/fda-ui/nuxt.config.js b/fda-ui/nuxt.config.js
index 0800379cf2485b00f0efa323a78bb227cb17dddb..27277ba5076a6e82f5dc30b4425d146455e1e6f8 100644
--- a/fda-ui/nuxt.config.js
+++ b/fda-ui/nuxt.config.js
@@ -78,24 +78,6 @@ export default {
     defaultPublisher
   },
 
-  proxy: {
-    '/api': api,
-    '/pid': {
-      target: api + '/api',
-      changeOrigin: true,
-      pathRewrite: {
-        '^/pid': '/pid'
-      }
-    },
-    '/retrieve': {
-      target: search,
-      changeOrigin: true,
-      pathRewrite: {
-        '^/retrieve': ''
-      }
-    }
-  },
-
   serverMiddleware: [
     { path: '/server-middleware', handler: path.resolve(__dirname, 'server-middleware/index.js') }
   ],
diff --git a/fda-ui/package.json b/fda-ui/package.json
index ff23e62ae94a9fff5f6ad2d24cc4092fa5e861d5..568ee827abb2ba00b51961cfac1c3110c9812147 100644
--- a/fda-ui/package.json
+++ b/fda-ui/package.json
@@ -47,6 +47,7 @@
     "qs": "^6.11.1",
     "sql-formatter": "^6.1.1",
     "vue": "^2.6.12",
+    "vue-axios": "^3.5.2",
     "vue-chartjs": "^4.1.1",
     "vue-jwt-decode": "^0.1.0",
     "vue-toast-notification": "^0.5.4",
diff --git a/fda-ui/pages/container/_container_id/database/_database_id/query/_query_id/index.vue b/fda-ui/pages/container/_container_id/database/_database_id/query/_query_id/index.vue
index b3349c8c4e362a8bf5f620ec2a7d74735486a3a6..042ef0a7f0faa3947759f1cc74748d7051eca41f 100644
--- a/fda-ui/pages/container/_container_id/database/_database_id/query/_query_id/index.vue
+++ b/fda-ui/pages/container/_container_id/database/_database_id/query/_query_id/index.vue
@@ -17,10 +17,10 @@
         <v-btn v-if="query.is_persisted && !query.identifier && canWrite" class="mb-1 mr-2" color="primary" :disabled="!executionUTC" @click.stop="openDialog()">
           <v-icon left>mdi-content-save-outline</v-icon> Get PID
         </v-btn>
-        <v-btn v-if="result_visibility && !query.identifier && query.result_number" class="mb-1" :loading="downloadLoading" @click.stop="downloadData">
+        <v-btn v-if="result_visibility && !query.identifier && query.result_number" class="mb-1" :loading="downloadLoading" @click.stop="downloadSubset">
           <v-icon left>mdi-download</v-icon> Data .csv
         </v-btn>
-        <v-btn v-if="result_visibility && query.identifier && query.result_number" class="mb-1" :loading="downloadLoading" @click.stop="download('text/csv')">
+        <v-btn v-if="result_visibility && query.identifier && query.result_number" class="mb-1" :loading="downloadLoading" @click.stop="downloadMetadata('text/csv')">
           <v-icon left>mdi-download</v-icon> Data .csv
         </v-btn>
         <v-btn
@@ -28,7 +28,7 @@
           color="secondary"
           class="ml-2"
           :loading="metadataLoading"
-          @click.stop="download('text/xml')">
+          @click.stop="downloadMetadata('text/xml')">
           <v-icon left>mdi-code-tags</v-icon> Metadata .xml
         </v-btn>
       </v-toolbar-title>
@@ -373,38 +373,33 @@ export default {
       this.$refs.queryResults.reExecute(this.query.id)
       this.$refs.queryResults.reExecuteCount(this.query.id)
     },
-    async download (mime) {
+    downloadMetadata (mime) {
       if (mime === 'text/csv') {
         this.downloadLoading = true
       } else if (mime === 'text/xml') {
         this.metadataLoading = true
       }
-      try {
-        const config = this.config
-        config.headers.Accept = mime
-        const res = await this.$axios.get(`/api/pid/${this.query.identifier.id}`, config)
-        console.debug('export identifier', res)
-        const url = window.URL.createObjectURL(new Blob([res.data]))
-        const link = document.createElement('a')
-        link.href = url
-        if (mime === 'text/csv') {
-          link.setAttribute('download', 'subset.csv')
-        } else if (mime === 'text/xml') {
-          link.setAttribute('download', 'identifier.xml')
-        }
-        document.body.appendChild(link)
-        link.click()
-      } catch (err) {
-        console.error('Could not export identifier', err)
-        this.$toast.error('Could not export identifier')
-        this.error = true
-      }
-      this.downloadLoading = false
-      this.metadataLoading = false
+      QueryService.exportMetadata(this.query.identifier.id, mime)
+        .then((metadata) => {
+          const url = window.URL.createObjectURL(new Blob([metadata]))
+          const link = document.createElement('a')
+          link.href = url
+          if (mime === 'text/csv') {
+            link.setAttribute('download', 'subset.csv')
+          } else if (mime === 'text/xml') {
+            link.setAttribute('download', 'identifier.xml')
+          }
+          document.body.appendChild(link)
+          link.click()
+        })
+        .finally(() => {
+          this.downloadLoading = false
+          this.metadataLoading = false
+        })
     },
-    downloadData () {
+    downloadSubset () {
       this.downloadLoading = true
-      QueryService.export(this.$route.params.container_id, this.$route.params.database_id, this.$route.params.query_id)
+      QueryService.exportSubset(this.$route.params.container_id, this.$route.params.database_id, this.$route.params.query_id)
         .then((data) => {
           const url = window.URL.createObjectURL(new Blob([data]))
           const link = document.createElement('a')
@@ -419,13 +414,17 @@ export default {
     },
     loadQuery () {
       this.loadingQuery = true
-      QueryService.findOne(this.$route.params.container_id, this.$route.params.database_id, this.$route.params.query_id)
-        .then((query) => {
-          this.query = query
-        })
-        .finally(() => {
-          this.loadingQuery = false
-        })
+      return new Promise((resolve, reject) => {
+        QueryService.findOne(this.$route.params.container_id, this.$route.params.database_id, this.$route.params.query_id)
+          .then((query) => {
+            this.query = query
+            resolve(query)
+          })
+          .catch(error => reject(error))
+          .finally(() => {
+            this.loadingQuery = false
+          })
+      })
     },
     save () {
       this.loadingSave = true
diff --git a/fda-ui/pages/container/_container_id/database/_database_id/table/_table_id/data.vue b/fda-ui/pages/container/_container_id/database/_database_id/table/_table_id/data.vue
index aa865b1e30c5f20f768978e77e64f8d01a70efc5..59e8aa9acf679c91e21f5f02161e790a4ace0cb5 100644
--- a/fda-ui/pages/container/_container_id/database/_database_id/table/_table_id/data.vue
+++ b/fda-ui/pages/container/_container_id/database/_database_id/table/_table_id/data.vue
@@ -1,5 +1,5 @@
 <template>
-  <div v-if="canRead">
+  <div v-if="canViewTableData">
     <TableToolbar :selection="selection" @modified="modified" />
     <v-toolbar :color="versionColor" flat>
       <v-toolbar-title>
@@ -89,6 +89,9 @@ export default {
     token () {
       return this.$store.state.token
     },
+    roles () {
+      return this.$store.state.roles
+    },
     database () {
       return this.$store.state.database
     },
@@ -156,11 +159,15 @@ export default {
       }
       return this.access.type === 'write_all'
     },
-    canRead () {
-      if (this.database?.is_public) {
+    canViewTableData () {
+      /* view when database is public or when private: 1) view-table-data role present 2) access is at least read */
+      if (!this.database) {
+        return false
+      }
+      if (this.database.is_public) {
         return true
       }
-      if (!this.user || !this.access) {
+      if (!this.roles || !this.roles.includes('view-table-data') || !this.access) {
         return false
       }
       return this.access.type === 'read' || this.access.type === 'write_own' || this.access.type === 'write_all'
diff --git a/fda-ui/pages/container/_container_id/database/_database_id/table/_table_id/import.vue b/fda-ui/pages/container/_container_id/database/_database_id/table/_table_id/import.vue
index d7358dac361459693a72e58ba05d98906621f231..d7ddea1caf6b9eccccce3656b2b52ba60750ef7a 100644
--- a/fda-ui/pages/container/_container_id/database/_database_id/table/_table_id/import.vue
+++ b/fda-ui/pages/container/_container_id/database/_database_id/table/_table_id/import.vue
@@ -1,5 +1,5 @@
 <template>
-  <div v-if="isResearcher">
+  <div v-if="canInsertTableData">
     <v-toolbar flat>
       <v-toolbar-title>
         <v-btn id="back-btn" class="mr-2" :to="`/container/${$route.params.container_id}/database/${$route.params.database_id}/table`">
@@ -10,7 +10,7 @@
         {{ table.name }}
       </v-toolbar-title>
     </v-toolbar>
-    <v-stepper v-model="step" vertical flat>
+    <v-stepper v-model="step" vertical flat tile>
       <v-stepper-step :complete="step > 1" step="1">
         Import Data
       </v-stepper-step>
@@ -96,10 +96,14 @@
       </v-stepper-content>
     </v-stepper>
     <v-breadcrumbs :items="items" class="pa-0 mt-2" />
+    <pre>loading={{ loading }}</pre>
   </div>
 </template>
 <script>
-const { isNonNegativeInteger, isResearcher } = require('@/utils')
+import TableService from '@/api/table.service'
+import MiddlewareService from '@/api/middleware.service'
+import QueryService from '@/api/query.service'
+const { isNonNegativeInteger } = require('@/utils')
 
 export default {
   name: 'TableImportCSV',
@@ -149,12 +153,6 @@ export default {
     }
   },
   computed: {
-    tableId () {
-      return this.$route.params.table_id
-    },
-    databaseId () {
-      return this.$route.params.database_id
-    },
     token () {
       return this.$store.state.token
     },
@@ -169,14 +167,14 @@ export default {
     user () {
       return this.$store.state.user
     },
-    isResearcher () {
-      return isResearcher(this.user)
-    },
-    fileConfig () {
-      return { headers: { 'Content-Type': 'multipart/form-data' } }
+    roles () {
+      return this.$store.state.roles
     },
-    sharedFilesystem () {
-      return this.$config.sharedFilesystem
+    canInsertTableData () {
+      if (!this.roles) {
+        return false
+      }
+      return this.roles.includes('insert-table-data')
     }
   },
   mounted () {
@@ -185,54 +183,36 @@ export default {
   methods: {
     isNonNegativeInteger,
     uploadAndImport () {
-      this.upload()
-        .then(() => this.import())
+      this.loading = true
+      MiddlewareService.upload(this.fileModel)
+        .then((file) => {
+          this.file = file
+          this.tableImport.location = `/tmp/${this.file.filename}`
+          QueryService.importCsv(this.$route.params.container_id, this.$route.params.database_id, this.$route.params.table_id, this.tableImport)
+            .then(() => {
+              this.$toast.success('Successfully imported data')
+              this.$router.push(`/container/${this.$route.params.container_id}/database/${this.$route.params.database_id}/table/${this.$route.params.table_id}`)
+            })
+            .finally(() => {
+              this.loading = false
+            })
+        })
+        .catch(() => {
+          this.loading = false
+        })
     },
     submit () {
       this.$refs.form.validate()
     },
-    async loadTableMetadata () {
-      this.loading = true
-      try {
-        const res = await this.$axios.get(`/api/container/${this.$route.params.container_id}/database/${this.databaseId}/table/${this.tableId}`, this.config)
-        console.debug('got table', res.data)
-        this.table = res.data
-      } catch (err) {
-        console.error('Could not insert data.', err)
-      }
-      this.loading = false
-    },
-    async upload () {
-      this.loading = true
-      const data = new FormData()
-      data.append('file', this.fileModel)
-      try {
-        const res = await this.$axios.post('/server-middleware/upload', data, this.fileConfig)
-        console.debug('file upload', res.data)
-        this.file = res.data
-      } catch (err) {
-        console.error('Failed to upload .csv data', err)
-        console.debug('failed to upload .csv data, does the .csv contain a header line?')
-        this.$toast.error('Could not upload data')
-      }
-      this.loading = false
-    },
-    async import () {
+    loadTableMetadata () {
       this.loading = true
-      const insertUrl = `/api/container/${this.$route.params.container_id}/database/${this.databaseId}/table/${this.tableId}/data/import`
-      this.tableImport.location = `/tmp/${this.file.filename}`
-      let insertResult
-      try {
-        insertResult = await this.$axios.post(insertUrl, this.tableImport, this.config)
-        console.debug('imported data', insertResult.data)
-      } catch (err) {
-        console.error('Could not import data.', err)
-        this.loading = false
-        return
-      }
-      this.$toast.success('Successfully imported data')
-      this.loading = false
-      this.$router.push(`/container/${this.$route.params.container_id}/database/${this.$route.params.database_id}/table/${this.$route.params.table_id}`)
+      TableService.findOne(this.$route.params.container_id, this.$route.params.database_id, this.$route.params.table_id)
+        .then((table) => {
+          this.table = table
+        })
+        .finally(() => {
+          this.loading = false
+        })
     }
   }
 }
diff --git a/fda-ui/pages/container/_container_id/database/_database_id/table/import.vue b/fda-ui/pages/container/_container_id/database/_database_id/table/import.vue
index 4c259e20b0458fc8b9aae167332688b324435a9d..72a19a252f0c02ed3c30210f101a363ad4585e22 100644
--- a/fda-ui/pages/container/_container_id/database/_database_id/table/import.vue
+++ b/fda-ui/pages/container/_container_id/database/_database_id/table/import.vue
@@ -1,9 +1,16 @@
 <template>
   <div v-if="canInsertTableData">
     <v-toolbar flat>
-      <v-toolbar-title>Create Table Schema (and Import Data) from .csv/.tsv</v-toolbar-title>
+      <v-toolbar-title>
+        <v-btn id="back-btn" class="mr-2" :to="`/container/${$route.params.container_id}/database/${$route.params.database_id}/table`">
+          <v-icon left>mdi-arrow-left</v-icon>
+        </v-btn>
+      </v-toolbar-title>
+      <v-toolbar-title>
+        Create Table Schema (and Import Data) from .csv/.tsv
+      </v-toolbar-title>
     </v-toolbar>
-    <v-stepper v-model="step" vertical flat>
+    <v-stepper v-model="step" vertical flat tile>
       <v-stepper-step :complete="step > 1" step="1">
         Table Information
       </v-stepper-step>
@@ -166,7 +173,7 @@
         Table Schema
       </v-stepper-step>
       <v-stepper-content step="4">
-        <TableSchema :back="true" :error="error" :columns="tableCreate.columns" @close="schemaClose" />
+        <TableSchema :back="true" :error="error" :loading="loading" :columns="tableCreate.columns" @close="schemaClose" />
       </v-stepper-content>
       <v-stepper-step
         :complete="step > 5"
@@ -189,7 +196,8 @@ import TableSchema from '@/components/TableSchema'
 import { notEmpty, isNonNegativeInteger, isResearcher } from '@/utils'
 import ContainerService from '@/api/container.service'
 import TableService from '@/api/table.service'
-import { determineDataTypes } from '@/api/analyse'
+import MiddlewareService from '@/api/middleware.service'
+import AnalyseService from '@/api/analyse.service'
 
 export default {
   name: 'TableFromCSV',
@@ -312,47 +320,44 @@ export default {
     submit () {
       this.$refs.form.validate()
     },
-    async upload () {
+    upload () {
       this.loading = true
-      const data = new FormData()
-      data.append('file', this.fileModel)
-      try {
-        const res = await this.$axios.post('/server-middleware/upload', data, this.fileConfig)
-        console.debug('file upload', res.data)
-        this.file = res.data
-      } catch (err) {
-        console.error('Failed to upload .csv data', err)
-        this.$toast.error('Could not upload data')
-      }
-      this.loading = false
+      return new Promise((resolve, reject) => {
+        MiddlewareService.upload(this.fileModel)
+          .then((file) => {
+            this.file = file
+            resolve(file)
+          })
+          .catch((error) => {
+            reject(error)
+          })
+          .finally(() => {
+            this.loading = false
+          })
+      })
     },
-    async analyse () {
+    analyse () {
       this.loading = true
-      try {
-        const res = await determineDataTypes(this.token, `/tmp/${this.file.filename}`)
-        const { columns } = res.data
-        console.log('data analyse result', columns)
-        this.tableCreate.columns = Object.entries(columns)
-          .map(([key, val]) => {
-            return {
-              name: key,
-              type: val,
-              null_allowed: true,
-              primary_key: false,
-              enum_values: []
-            }
-          })
-        this.tableImport.location = `/tmp/${this.file.filename}`
-        this.step = 4
-        this.loading = false
-        console.debug('upload csv', res.data)
-        return
-      } catch (err) {
-        console.error('Failed to upload .csv data', err)
-        console.debug('failed to upload .csv data, does the .csv contain a header line?')
-        this.$toast.error('Could not upload data')
-      }
-      this.loading = false
+      AnalyseService.determineDataTypes(`/tmp/${this.file.filename}`)
+        .then((analysis) => {
+          const { columns } = analysis
+          this.tableCreate.columns = Object.entries(columns)
+            .map(([key, val]) => {
+              return {
+                name: key,
+                type: val,
+                null_allowed: true,
+                primary_key: false,
+                enum_values: []
+              }
+            })
+          this.tableImport.location = `/tmp/${this.file.filename}`
+          this.step = 4
+          this.loading = false
+        })
+        .finally(() => {
+          this.loading = false
+        })
     },
     listTables () {
       this.loading = true
@@ -371,6 +376,7 @@ export default {
         return
       }
       this.validStep4 = true
+      this.step = 5
       this.createTable()
     },
     setOthers (column) {
@@ -423,4 +429,13 @@ export default {
 </script>
 
 <style scoped>
+#back-btn {
+  min-width: auto;
+  padding: 0 0 0 12px;
+  background: none !important;
+  box-shadow: none;
+}
+#back-btn::before {
+  opacity: 0;
+}
 </style>
diff --git a/fda-ui/pages/login.vue b/fda-ui/pages/login.vue
index 66351469bcc28af905037d164153f1b2d6b5e24c..d7d26e64a919a0c98d63db1a0973502f903eca92 100644
--- a/fda-ui/pages/login.vue
+++ b/fda-ui/pages/login.vue
@@ -108,7 +108,7 @@ export default {
           UserService.findOne(userId)
             .then((user) => {
               this.$store.commit('SET_USER', user)
-              this.$vuetify.theme.dark = UserMapper.getThemeDark(this.user)
+              this.$vuetify.theme.dark = user.attributes.theme_dark
               this.$router.push('/container')
             })
         })
diff --git a/fda-ui/pages/search/index.vue b/fda-ui/pages/search/index.vue
index a466a0f9229d395bfd03be5e671e250eec482079..d13f084ad5eaee140ee3a3503b7eb9c66aa46757 100644
--- a/fda-ui/pages/search/index.vue
+++ b/fda-ui/pages/search/index.vue
@@ -7,6 +7,7 @@
     <v-card
       v-for="(result, idx) in results"
       :key="idx"
+      :to="link(result)"
       flat
       tile>
       <v-divider class="mx-4" />
diff --git a/fda-ui/pages/user/developer.vue b/fda-ui/pages/user/developer.vue
deleted file mode 100644
index 7199d562b745dc63ef6aff2f6d926a2170eca0c7..0000000000000000000000000000000000000000
--- a/fda-ui/pages/user/developer.vue
+++ /dev/null
@@ -1,234 +0,0 @@
-<template>
-  <div>
-    <UserToolbar />
-    <v-tabs-items v-model="tab">
-      <v-tab-item>
-        <v-card flat tile>
-          <v-card-title>Personal Access Tokens</v-card-title>
-          <v-card-text>
-            <v-list-item v-for="(item, i) in tokens" :key="i" three-line>
-              <v-list-item-content>
-                <v-list-item-title :class="tokenClass(item)">sha256:{{ item.token_hash }}</v-list-item-title>
-                <v-list-item-subtitle v-if="!item.token" :class="tokenClass(item)">
-                  Last used: <span v-if="item.last_used">{{ format(item.last_used) }}</span><span v-if="!item.last_used">Never</span> &mdash; valid until: {{ format(item.expires) }}
-                </v-list-item-subtitle>
-                <v-list-item-subtitle v-if="item.token">
-                  <v-text-field
-                    v-model="item.token"
-                    :append-outer-icon="item.copied ? 'mdi-check' : 'mdi-content-copy'"
-                    readonly
-                    hint="Copy this token, it will not be visible again!"
-                    persistent-hint
-                    type="text"
-                    @click:append-outer="copy(item)" />
-                </v-list-item-subtitle>
-                <v-list-item-subtitle v-if="!item.token">
-                  <a @click="revokeToken(item.id)">Revoke Token</a>
-                </v-list-item-subtitle>
-              </v-list-item-content>
-            </v-list-item>
-            <v-btn
-              v-if="isResearcher || isDeveloper"
-              :disabled="tokens.length >= tokenMax"
-              class="mt-4"
-              color="secondary"
-              small
-              @click="mintToken">
-              Create Token
-            </v-btn>
-          </v-card-text>
-          <v-divider v-if="isDeveloper" />
-          <v-card-title v-if="isDeveloper">User Roles</v-card-title>
-          <v-card-subtitle v-if="isDeveloper">Modify user roles</v-card-subtitle>
-          <v-data-table
-            v-if="isDeveloper"
-            :headers="headers"
-            :items="users"
-            :loading="loadingUsers"
-            :items-per-page="10">
-            <template v-slot:item.username="{ item }">
-              {{ item.username }}
-            </template>
-            <template v-slot:item.roles="{ item }">
-              <div v-for="(role, idx) in item.roles" :key="idx">
-                {{ formatRole(role) }}
-              </div>
-            </template>
-            <template v-slot:item.action="{ item }">
-              <v-btn
-                v-if="item.username !== user.username"
-                :disabled="isDeveloper1(item)"
-                x-small
-                @click="modifyRoles(item)">
-                Modify
-              </v-btn>
-              <span v-if="item.username === user.username">(you)</span>
-            </template>
-          </v-data-table>
-        </v-card>
-      </v-tab-item>
-    </v-tabs-items>
-    <v-dialog
-      v-model="editRoleDialog"
-      persistent
-      max-width="640">
-      <EditRoles :user="selectedUser" @close-dialog="closeDialog" />
-    </v-dialog>
-  </div>
-</template>
-
-<script>
-import { formatTimestamp, isResearcher, isDeveloper } from '@/utils'
-import UserToolbar from '@/components/UserToolbar'
-import EditRoles from '@/components/dialogs/EditRoles'
-
-export default {
-  components: {
-    EditRoles,
-    UserToolbar
-  },
-  data () {
-    return {
-      tab: 0,
-      error: false,
-      tokens: [],
-      loading: false,
-      loadingUsers: false,
-      users: [],
-      editRoleDialog: false,
-      selectedUser: {},
-      roles: [
-        { text: 'Researcher', value: 'researcher', code: 'ROLE_RESEARCHER' },
-        { text: 'Data Steward', value: 'data_steward', code: 'ROLE_DATA_STEWARD' },
-        { text: 'Developer', value: 'developer', code: 'ROLE_DEVELOPER' }
-      ]
-    }
-  },
-  computed: {
-    token () {
-      return this.$store.state.token
-    },
-    user () {
-      return this.$store.state.user
-    },
-    headers () {
-      return [
-        { text: 'Username', value: 'username', sortable: false },
-        { text: 'Role', value: 'roles', sortable: false },
-        { text: 'Action', value: 'action', sortable: false }
-      ]
-    },
-    isDeveloper () {
-      return isDeveloper(this.user)
-    },
-    isResearcher () {
-      return isResearcher(this.user)
-    },
-    config () {
-      if (this.token === null) {
-        return {}
-      }
-      return {
-        headers: { Authorization: `Bearer ${this.token}` }
-      }
-    },
-    tokenMax () {
-      return this.$config.tokenMax
-    }
-  },
-  mounted () {
-    this.loadTokens()
-    this.loadUsers()
-  },
-  methods: {
-    submit () {
-    },
-    copy (item) {
-      item.copied = true
-      navigator.clipboard.writeText(item.token)
-    },
-    format (timestamp) {
-      return formatTimestamp(timestamp)
-    },
-    tokenClass (token) {
-      return token.last_used ? '' : 'token-not_used'
-    },
-    isDeveloper1 (user) {
-      return isDeveloper(user)
-    },
-    closeDialog (event) {
-      if (event.success) {
-        this.loadUsers()
-      }
-      this.editRoleDialog = false
-    },
-    modifyRoles (item) {
-      this.selectedUser = item
-      this.editRoleDialog = true
-    },
-    async loadTokens () {
-      this.loading = true
-      try {
-        const res = await this.$axios.get('/api/user/token', this.config)
-        this.tokens = res.data.filter(t => !t.deleted)
-        console.debug('tokens', this.tokens)
-      } catch (err) {
-        this.$toast.error('Could not load tokens')
-      }
-      this.loading = false
-    },
-    async mintToken () {
-      this.loading = true
-      try {
-        const res = await this.$axios.post('/api/user/token', {}, this.config)
-        const token = res.data
-        token.copied = false
-        console.debug('token', token)
-        this.tokens.push(token)
-      } catch (err) {
-        if (err.response.status === 417) {
-          this.$toast.error('Already exceeded the maximum allowed number of tokens!')
-        } else {
-          this.$toast.error('Could not create token')
-        }
-      }
-      this.loading = false
-    },
-    formatRole (role) {
-      if (role === null) {
-        return null
-      }
-      const arr = this.roles.filter(r => r.code === role)
-      return arr.length > 0 ? arr[0].text : null
-    },
-    async loadUsers () {
-      this.loadingUsers = true
-      try {
-        const res = await this.$axios.get('/api/user', this.config)
-        this.users = res.data
-        console.debug('users', this.users)
-      } catch (error) {
-        const { message } = error.response
-        this.$toast.error('Failed to load users: ' + message)
-        console.error('Failed to load users', error)
-      }
-      this.loadingUsers = false
-    },
-    async revokeToken (id) {
-      this.loading = true
-      try {
-        await this.$axios.delete(`/api/user/token/${id}`, this.config)
-        await this.loadTokens()
-      } catch (err) {
-        this.$toast.error('Could not delete token')
-      }
-      this.loading = false
-    }
-  }
-}
-</script>
-<style>
-.token-not_used {
-  opacity: 0.4;
-}
-</style>
diff --git a/fda-ui/plugins/axios.js b/fda-ui/plugins/axios.js
index 5de5da3b202469b256c7ab811d438e21f170df42..bdfd8d1aca860e614a2d7b5fa5504104451a990b 100644
--- a/fda-ui/plugins/axios.js
+++ b/fda-ui/plugins/axios.js
@@ -3,6 +3,7 @@ import store from '@/store'
 import api from '@/api'
 import AuthenticationService from '@/api/authentication.service'
 import jwtDecode from 'jwt-decode'
+import VueAxios from 'vue-axios'
 
 api.interceptors.request.use((config) => {
   const token = store().state.token
@@ -30,4 +31,4 @@ api.interceptors.request.use((config) => {
   return config
 })
 
-Vue.use(api)
+Vue.use(VueAxios, api)
diff --git a/fda-ui/yarn.lock b/fda-ui/yarn.lock
index bff2f60529e64d54be07f685b751033d20e69b24..2a71a6d3aefb0114bc97aa58dc855a878efd730b 100644
--- a/fda-ui/yarn.lock
+++ b/fda-ui/yarn.lock
@@ -12235,6 +12235,11 @@ vm-browserify@^1.0.1:
   resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-1.1.2.tgz#78641c488b8e6ca91a75f511e7a3b32a86e5dda0"
   integrity sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==
 
+vue-axios@^3.5.2:
+  version "3.5.2"
+  resolved "https://registry.yarnpkg.com/vue-axios/-/vue-axios-3.5.2.tgz#28637524cca550a9e97197e85a41930ec63604d5"
+  integrity sha512-GP+dct7UlAWkl1qoP3ppw0z6jcSua5/IrMpjB5O8bh089iIiJ+hdxPYH2NPEpajlYgkW5EVMP95ttXWdas1O0g==
+
 vue-chartjs@^4.1.1:
   version "4.1.1"
   resolved "https://registry.yarnpkg.com/vue-chartjs/-/vue-chartjs-4.1.1.tgz#b1ffc2845e09d14cb5255305b11bd3e8df8058ab"