Переглянути джерело

[FE] Implement a full support for custom context path (#3106)

* Vite Custom Plugin code initialization , ( not building on js file )

* Fix the lazy loading functionality in vite

* add lazy loading in the Cluster page

* minor comment code cleanup

* Remove un-necessary files for configuration of the vite

* minor naming modifications

* minor code modification to encode a variable in the root
Mgrdich 2 роки тому
батько
коміт
9d98927524

+ 2 - 4
kafka-ui-api/src/main/java/com/provectus/kafka/ui/controller/StaticController.java

@@ -53,9 +53,7 @@ public class StaticController {
   @SneakyThrows
   private String buildFile(Resource file, String contextPath) {
     return ResourceUtil.readAsString(file)
-        .replace("\"/assets/", "\"" + contextPath + "/assets/")
-        .replace("\"/favicon/", "\"" + contextPath + "/favicon/")
-        .replace("/manifest.json", contextPath + "/manifest.json")
-        .replace("window.basePath = ''", "window.basePath=\"" + contextPath + "\"");
+        .replace("\"assets/", "\"" + contextPath + "/assets/")
+        .replace("PUBLIC-PATH-VARIABLE",  contextPath);
   }
 }

+ 9 - 6
kafka-ui-react-app/index.html

@@ -3,7 +3,6 @@
   <head>
     <meta charset="utf-8" />
     <meta name="viewport" content="width=device-width, initial-scale=1" />
-
     <!-- Google fonts -->
     <link rel="preconnect" href="https://fonts.googleapis.com" />
     <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
@@ -13,14 +12,18 @@
     />
 
     <!-- Favicons -->
-    <link rel="icon" href="/favicon/favicon.ico" sizes="any" />
-    <link rel="icon" href="/favicon/icon.svg" type="image/svg+xml" />
-    <link rel="apple-touch-icon" href="/favicon/apple-touch-icon.png" />
-    <link rel="manifest" href="/manifest.json" />
+    <link rel="icon" href="PUBLIC-PATH-VARIABLE/favicon/favicon.ico" sizes="any" />
+    <link rel="icon" href="PUBLIC-PATH-VARIABLE/favicon/icon.svg" type="image/svg+xml" />
+    <link rel="apple-touch-icon" href="PUBLIC-PATH-VARIABLE/favicon/apple-touch-icon.png" />
+    <link rel="manifest" href="PUBLIC-PATH-VARIABLE/manifest.json" />
 
     <title>UI for Apache Kafka</title>
     <script type="text/javascript">
-      window.basePath = '';
+      window.basePath = 'PUBLIC-PATH-VARIABLE';
+
+      window.__assetsPathBuilder = function (importer) {
+        return window.basePath+ "/" + importer;
+      };
     </script>
   </head>
 

+ 45 - 51
kafka-ui-react-app/src/components/Cluster/Cluster.tsx

@@ -16,23 +16,15 @@ import {
 import ClusterContext from 'components/contexts/ClusterContext';
 import PageLoader from 'components/common/PageLoader/PageLoader';
 import { useClusters } from 'lib/hooks/api/clusters';
-import Brokers from 'components/Brokers/Brokers';
-import Topics from 'components/Topics/Topics';
-import Schemas from 'components/Schemas/Schemas';
-import Connect from 'components/Connect/Connect';
-import KsqlDb from 'components/KsqlDb/KsqlDb';
-import ConsumerGroups from 'components/ConsumerGroups/ConsumerGroups';
 
-// We can't use Lazy loading till we have a better way to update publicPath in runtime
-// Now java app replaces paths in builded index.html file.
-// const Brokers = React.lazy(() => import('components/Brokers/Brokers'));
-// const Topics = React.lazy(() => import('components/Topics/Topics'));
-// const Schemas = React.lazy(() => import('components/Schemas/Schemas'));
-// const Connect = React.lazy(() => import('components/Connect/Connect'));
-// const KsqlDb = React.lazy(() => import('components/KsqlDb/KsqlDb'));
-// const ConsumerGroups = React.lazy(
-//   () => import('components/ConsumerGroups/ConsumerGroups')
-// );
+const Brokers = React.lazy(() => import('components/Brokers/Brokers'));
+const Topics = React.lazy(() => import('components/Topics/Topics'));
+const Schemas = React.lazy(() => import('components/Schemas/Schemas'));
+const Connect = React.lazy(() => import('components/Connect/Connect'));
+const KsqlDb = React.lazy(() => import('components/KsqlDb/KsqlDb'));
+const ConsumerGroups = React.lazy(
+  () => import('components/ConsumerGroups/ConsumerGroups')
+);
 
 const Cluster: React.FC = () => {
   const { clusterName } = useAppParams<ClusterNameRoute>();
@@ -59,49 +51,51 @@ const Cluster: React.FC = () => {
   return (
     <Suspense fallback={<PageLoader />}>
       <ClusterContext.Provider value={contextValue}>
-        <Routes>
-          <Route
-            path={getNonExactPath(clusterBrokerRelativePath)}
-            element={<Brokers />}
-          />
-          <Route
-            path={getNonExactPath(clusterTopicsRelativePath)}
-            element={<Topics />}
-          />
-          <Route
-            path={getNonExactPath(clusterConsumerGroupsRelativePath)}
-            element={<ConsumerGroups />}
-          />
-          {contextValue.hasSchemaRegistryConfigured && (
+        <Suspense fallback={<PageLoader />}>
+          <Routes>
             <Route
-              path={getNonExactPath(clusterSchemasRelativePath)}
-              element={<Schemas />}
+              path={getNonExactPath(clusterBrokerRelativePath)}
+              element={<Brokers />}
             />
-          )}
-          {contextValue.hasKafkaConnectConfigured && (
             <Route
-              path={getNonExactPath(clusterConnectsRelativePath)}
-              element={<Connect />}
+              path={getNonExactPath(clusterTopicsRelativePath)}
+              element={<Topics />}
             />
-          )}
-          {contextValue.hasKafkaConnectConfigured && (
             <Route
-              path={getNonExactPath(clusterConnectorsRelativePath)}
-              element={<Connect />}
+              path={getNonExactPath(clusterConsumerGroupsRelativePath)}
+              element={<ConsumerGroups />}
             />
-          )}
-          {contextValue.hasKsqlDbConfigured && (
+            {contextValue.hasSchemaRegistryConfigured && (
+              <Route
+                path={getNonExactPath(clusterSchemasRelativePath)}
+                element={<Schemas />}
+              />
+            )}
+            {contextValue.hasKafkaConnectConfigured && (
+              <Route
+                path={getNonExactPath(clusterConnectsRelativePath)}
+                element={<Connect />}
+              />
+            )}
+            {contextValue.hasKafkaConnectConfigured && (
+              <Route
+                path={getNonExactPath(clusterConnectorsRelativePath)}
+                element={<Connect />}
+              />
+            )}
+            {contextValue.hasKsqlDbConfigured && (
+              <Route
+                path={getNonExactPath(clusterKsqlDbRelativePath)}
+                element={<KsqlDb />}
+              />
+            )}
             <Route
-              path={getNonExactPath(clusterKsqlDbRelativePath)}
-              element={<KsqlDb />}
+              path="/"
+              element={<Navigate to={clusterBrokerRelativePath} replace />}
             />
-          )}
-          <Route
-            path="/"
-            element={<Navigate to={clusterBrokerRelativePath} replace />}
-          />
-        </Routes>
-        <Outlet />
+          </Routes>
+          <Outlet />
+        </Suspense>
       </ClusterContext.Provider>
     </Suspense>
   );

+ 4 - 1
kafka-ui-react-app/src/components/Cluster/__tests__/Cluster.spec.tsx

@@ -1,7 +1,7 @@
 import React from 'react';
 import { Cluster, ClusterFeaturesEnum } from 'generated-sources';
 import ClusterComponent from 'components/Cluster/Cluster';
-import { screen } from '@testing-library/react';
+import { screen, waitFor } from '@testing-library/react';
 import { render, WithRoute } from 'lib/testHelpers';
 import {
   clusterBrokersPath,
@@ -59,6 +59,9 @@ describe('Cluster', () => {
       </WithRoute>,
       { initialEntries: [pathname] }
     );
+    await waitFor(() => {
+      expect(screen.queryByRole('progressbar')).not.toBeInTheDocument();
+    });
   };
 
   it('renders Brokers', async () => {

+ 20 - 0
kafka-ui-react-app/vite.config.ts

@@ -18,6 +18,26 @@ export default defineConfig(({ mode }) => {
     build: {
       outDir: 'build',
     },
+    experimental: {
+      renderBuiltUrl(
+        filename: string,
+        {
+          hostType,
+        }: {
+          hostId: string;
+          hostType: 'js' | 'css' | 'html';
+          type: 'asset' | 'public';
+        }
+      ) {
+        if (hostType === 'js') {
+          return {
+            runtime: `window.__assetsPathBuilder(${JSON.stringify(filename)})`,
+          };
+        }
+
+        return filename;
+      },
+    },
     define: {
       'process.env.NODE_ENV': `"${mode}"`,
       'process.env.VITE_TAG': `"${process.env.VITE_TAG}"`,