Skip to content
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion apps/assisted-ui/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ $ podman build -t quay.io/edge-infrastructure/assisted-installer-ui:latest . --b
You can run the standalone UI with chatbot enabled. You need to be logged in via `ocm`.

```
$ AIUI_CHAT_API_URL=<chatbot_url> AIUI_OCM_TOKEN=$(ocm token) yarn start:assisted_ui
$ AIUI_CHAT_API_URL=<chatbot_url> OCM_REFRESH_TOKEN=$(ocm token --refresh) AIUI_SSO_API_URL=https://sso.redhat.com/auth/realms/redhat-external/protocol/openid-connect/token yarn start:assisted_ui
```

## Available Scripts
Expand Down
14 changes: 13 additions & 1 deletion apps/assisted-ui/deploy/nginx.conf
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ location /api {
}

location /chatbot/ {
proxy_pass $AIUI_CHAT_API_URL/;
proxy_pass $AIUI_CHAT_API_URL;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
Expand All @@ -28,6 +28,18 @@ location /chatbot/ {
client_max_body_size 2M;
}

location /token {
proxy_pass $AIUI_SSO_API_URL;
proxy_http_version 1.1;
proxy_cache_bypass $http_upgrade;
proxy_connect_timeout 120;
proxy_send_timeout 120;
proxy_read_timeout 120;
send_timeout 120;
client_max_body_size 2M;
proxy_ssl_server_name on;
}

location / {
try_files $uri /index.html;
}
7 changes: 4 additions & 3 deletions apps/assisted-ui/deploy/start.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,18 @@
set -eo pipefail

export ASSISTED_SERVICE_URL="${ASSISTED_SERVICE_URL:-'http://localhost:8090'}"
export AIUI_CHAT_API_URL="${AIUI_CHAT_API_URL:-'http://localhost:1234'}"
export AIUI_CHAT_API_URL="${AIUI_CHAT_API_URL:-'http://localhost:1234/'}"
export AIUI_SSO_API_URL="${AIUI_SSO_API_URL:-'http://localhost:1235'}"

# shellcheck disable=SC2016
envsubst '$ASSISTED_SERVICE_URL $AIUI_CHAT_API_URL' < /deploy/nginx.conf > "$NGINX_DEFAULT_CONF_PATH/nginx.conf"
envsubst '$ASSISTED_SERVICE_URL $AIUI_CHAT_API_URL $AIUI_SSO_API_URL' < /deploy/nginx.conf > "$NGINX_DEFAULT_CONF_PATH/nginx.conf"

if [ "$ASSISTED_SERVICE_SCHEME" = "https" ]; then
# shellcheck disable=SC2016
envsubst '${HTTPS_CERT_FILE} ${HTTPS_KEY_FILE}' < /deploy/nginx_ssl.conf > "${NGINX_CONF_PATH}"
fi

envsubst '$AIUI_OCM_TOKEN' < $NGINX_APP_ROOT/src/env.template.js > $NGINX_APP_ROOT/src/env.js
envsubst '$AIUI_OCM_REFRESH_TOKEN' < $NGINX_APP_ROOT/src/env.template.js > $NGINX_APP_ROOT/src/env.js

# Do not listen on IPv6 if it's not enabled in the hardware
if grep 'ipv6.disable=1' /proc/cmdline; then
Expand Down
2 changes: 1 addition & 1 deletion apps/assisted-ui/env.template.js
Original file line number Diff line number Diff line change
@@ -1 +1 @@
window.OCM_TOKEN = '${AIUI_OCM_TOKEN}';
window.OCM_REFRESH_TOKEN = '${AIUI_OCM_REFRESH_TOKEN}';
2 changes: 1 addition & 1 deletion apps/assisted-ui/public/env.js
Original file line number Diff line number Diff line change
@@ -1 +1 @@
window.OCM_TOKEN = '';
window.OCM_REFRESH_TOKEN = '';
4 changes: 2 additions & 2 deletions apps/assisted-ui/src/components/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { CompatRouter, Route } from 'react-router-dom-v5-compat';
import { Page } from '@patternfly/react-core';
import * as OCM from '@openshift-assisted/ui-lib/ocm';
import { Header } from './Header';
import ChatBot, { getOcmToken } from './Chatbot';
import ChatBot, { refreshToken } from './Chatbot';
import '../i18n';

const { HostsClusterDetailTabMock, UILibRoutes, Features, Config } = OCM;
Expand All @@ -16,7 +16,7 @@ export const App: React.FC = () => (
<Page header={<Header />} isManagedSidebar defaultManagedSidebarIsOpen={false}>
<UILibRoutes
allEnabledFeatures={Features.STANDALONE_DEPLOYMENT_ENABLED_FEATURES}
additionalComponents={getOcmToken() ? <ChatBot /> : undefined}
additionalComponents={refreshToken ? <ChatBot /> : undefined}
>
<Route path={'/day2-flow-mock'} element={<HostsClusterDetailTabMock />} />
</UILibRoutes>
Expand Down
45 changes: 42 additions & 3 deletions apps/assisted-ui/src/components/Chatbot.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,55 @@ import '@patternfly-6/react-core/dist/styles/base.css';
import '@patternfly-6/chatbot/dist/css/main.css';
import '@patternfly-6/patternfly/dist/patternfly-addons.css';

export const getOcmToken = () =>
(import.meta.env.AIUI_OCM_TOKEN as string | undefined) || window.OCM_TOKEN;
export const refreshToken =
(import.meta.env.AIUI_OCM_REFRESH_TOKEN as string | undefined) || window.OCM_REFRESH_TOKEN;
let expiration = Date.now();
let token = '';

export const getOcmToken = async () => {
// if token expires in less than 5s, refresh it
if (Date.now() - 5000 > expiration) {
if (!refreshToken) {
throw new Error('No refresh token available');
}
const params = new URLSearchParams();
params.append('grant_type', 'refresh_token');
params.append('refresh_token', refreshToken || '');
params.append('client_id', 'cloud-services');

try {
const response = await fetch('/token', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: params.toString(),
});

if (!response.ok) {
throw new Error(`Token refresh failed: ${response.status}`);
}

const data = (await response.json()) as { access_token: string; expires_in: number };
token = data.access_token;
expiration = Date.now() + data.expires_in * 1000;
} catch (error) {
// eslint-disable-next-line
console.error('Failed to refresh token:', error);
throw error;
}
}
return token;
};

const ChatBot = () => {
const onApiCall: ChatBotWindowProps['onApiCall'] = async (input, init) => {
const token = await getOcmToken();
return fetch(`/chatbot${input.toString()}`, {
...(init || {}),
headers: {
...(init?.headers || {}),
Authorization: `Bearer ${getOcmToken() || ''}`,
Authorization: `Bearer ${token}`,
},
});
};
Expand Down
2 changes: 1 addition & 1 deletion apps/assisted-ui/src/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { App } from './components/App';

declare global {
interface Window {
OCM_TOKEN?: string;
OCM_REFRESH_TOKEN?: string;
}
}

Expand Down
5 changes: 5 additions & 0 deletions apps/assisted-ui/vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,11 @@ export default defineConfig(async ({ mode }) => {
changeOrigin: true,
rewrite: (path: string) => path.replace(/^\/chatbot/, ''),
},
'/token': {
target: env.AIUI_SSO_API_URL,
changeOrigin: true,
rewrite: (path: string) => path.replace(/^\/token/, ''),
},
},
},
};
Expand Down
Loading