Skip to content
This repository has been archived by the owner on Dec 13, 2023. It is now read-only.

Feat: Enable hosting app in any route, with easy config #3722

Merged
merged 2 commits into from
Nov 29, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
8 changes: 8 additions & 0 deletions ui/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,14 @@ Your hosting environment should make the Conductor Server API available on the s

See `docker/serverAndUI` for an `nginx` based example.

#### Different host path
The static UI would work when rendered from any host route.
The default is '/'. You can customize this by setting the 'homepage' field in package.json
Refer
- https://docs.npmjs.com/cli/v9/configuring-npm/package-json#homepage
- https://create-react-app.dev/docs/deployment/#building-for-relative-paths


### Customization Hooks

For ease of maintenance, a number of touch points for customization have been removed to `/plugins`.
Expand Down
2 changes: 1 addition & 1 deletion ui/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "client",
"version": "3.7.2",
"version": "3.8.0",
"dependencies": {
"@material-ui/core": "^4.12.3",
"@material-ui/icons": "^4.11.2",
Expand Down
7 changes: 5 additions & 2 deletions ui/src/components/NavLink.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,14 @@ import { Link } from "@material-ui/core";
import LaunchIcon from "@material-ui/icons/Launch";
import Url from "url-parse";
import { useEnv } from "../plugins/env";
import { getBasename } from "../utils/helpers";
import { cleanDuplicateSlash } from "../plugins/fetch";

// 1. Strip `navigate` from props to prevent error
// 2. Preserve stack param

export default React.forwardRef((props, ref) => {
const { navigate, path, newTab, ...rest } = props;
const { navigate, path, newTab, absolutePath = false, ...rest } = props;
const { stack, defaultStack } = useEnv();

const url = new Url(path, {}, true);
Expand All @@ -24,8 +26,9 @@ export default React.forwardRef((props, ref) => {
</Link>
);
} else {
const href = absolutePath ? url.toString() : cleanDuplicateSlash(getBasename() + "/" + url.toString());
return (
<Link ref={ref} target="_blank" href={url.toString()}>
<Link ref={ref} target="_blank" href={href}>
{rest.children}
&nbsp;
<LaunchIcon fontSize="small" style={{ verticalAlign: "middle" }} />
Expand Down
3 changes: 2 additions & 1 deletion ui/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { BrowserRouter } from "react-router-dom";
import CssBaseline from "@material-ui/core/CssBaseline";
import { QueryClient, QueryClientProvider } from "react-query";
import { ReactQueryDevtools } from "react-query/devtools";
import { getBasename } from "./utils/helpers";

const queryClient = new QueryClient({
defaultOptions: {
Expand All @@ -22,7 +23,7 @@ ReactDOM.render(
//<React.StrictMode>
<QueryClientProvider client={queryClient}>
<ThemeProvider>
<BrowserRouter>
<BrowserRouter basename={getBasename()}>
<CssBaseline />
<ReactQueryDevtools initialIsOpen={true} />

Expand Down
5 changes: 4 additions & 1 deletion ui/src/plugins/AppLogo.jsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import React from "react";
import { makeStyles } from "@material-ui/core/styles";
import { getBasename } from "../utils/helpers";
import { cleanDuplicateSlash } from "./fetch";

const useStyles = makeStyles((theme) => ({
logo: {
Expand All @@ -11,5 +13,6 @@ const useStyles = makeStyles((theme) => ({

export default function AppLogo() {
const classes = useStyles();
return <img src="/logo.svg" alt="Conductor" className={classes.logo} />;
const logoPath = getBasename() + '/logo.svg';
return <img src={cleanDuplicateSlash(logoPath)} alt="Conductor" className={classes.logo} />;
}
10 changes: 8 additions & 2 deletions ui/src/plugins/fetch.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { getBasename } from "../utils/helpers";
import { useEnv } from "./env";

export function useFetchContext() {
Expand All @@ -15,8 +16,9 @@ export function fetchWithContext(
) {
const newParams = { ...fetchParams };

const newPath = `/api/${path}`;
const cleanPath = newPath.replace(/([^:]\/)\/+/g, "$1"); // Cleanup duplicated slashes
const basename = getBasename();
const newPath = basename + `/api/${path}`;
const cleanPath = cleanDuplicateSlash(newPath); // Cleanup duplicated slashes

return fetch(cleanPath, newParams)
.then((res) => Promise.all([res, res.text()]))
Expand All @@ -38,3 +40,7 @@ export function fetchWithContext(
}
});
}

export function cleanDuplicateSlash(path) {
return path.replace(/([^:]\/)\/+/g, "$1");
}
9 changes: 9 additions & 0 deletions ui/src/utils/helpers.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { format, formatDuration, intervalToDuration } from "date-fns";
import _ from "lodash";
import packageJson from '../../package.json';

export function timestampRenderer(date) {
if (_.isNil(date)) return null;
Expand Down Expand Up @@ -88,3 +89,11 @@ export function isEmptyIterable(iterable) {
}
return true;
}

export function getBasename() {
let basename = '/';
try{
basename = new URL(packageJson.homepage).pathname;
} catch(e) {}
return _.isEmpty(basename) ? '/' : basename;
}