This is an automated email from the ASF dual-hosted git repository.
yasith pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/airavata.git
The following commit(s) were added to refs/heads/master by this push:
new fe8d4e54f4 gateway switching for local agent (#469)
fe8d4e54f4 is described below
commit fe8d4e54f4d3a941dd1d245993a046c66d19e6e5
Author: Ganning Xu <[email protected]>
AuthorDate: Sat Apr 26 17:52:18 2025 -0400
gateway switching for local agent (#469)
* gateway switching for local agent
* add build.sh to gitignore
---
.gitignore | 1 +
airavata-local-agent/main/background.js | 71 +++++++++++++-
airavata-local-agent/package.json | 2 +-
.../renderer/components/HeaderBox.jsx | 4 +-
airavata-local-agent/renderer/lib/Contexts.js | 107 +++++++++++----------
airavata-local-agent/renderer/pages/_app.jsx | 6 +-
.../renderer/pages/docker-home.jsx | 4 +-
airavata-local-agent/renderer/pages/login.jsx | 55 ++++++++++-
8 files changed, 188 insertions(+), 62 deletions(-)
diff --git a/.gitignore b/.gitignore
index db30446109..e4bae615e4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -47,5 +47,6 @@ airavata-local-agent/proxy/config.txt
airavata-local-agent/proxy/websockify
airavata-local-agent/renderer/.next
airavata-local-agent/dist
+airavata-local-agent/build.sh
logs
diff --git a/airavata-local-agent/main/background.js
b/airavata-local-agent/main/background.js
index 9ca6241c5a..67623d5874 100644
--- a/airavata-local-agent/main/background.js
+++ b/airavata-local-agent/main/background.js
@@ -26,6 +26,8 @@ import serve from 'electron-serve';
import { createWindow } from './helpers';
const fs = require('fs');
import log from 'electron-log/main';
+import Store from 'electron-store';
+
const isProd = process.env.NODE_ENV === 'production';
@@ -38,7 +40,7 @@ if (isProd) {
let mainWindow;
const TOKEN_FILE = '~/csagent/token/keys.json';
-const BASE_URL = 'https://cybershuttle.org';
+let BASE_URL = 'this value will be replaced';
// ----- OUR CUSTOM FUNCTIONS -----
if (process.defaultApp) {
@@ -795,4 +797,69 @@ const writeFile = async (event, userPath, data) => {
ipcMain.on('write-file', writeFile);
/*
-*/
\ No newline at end of file
+GATEWAY PINGING LOGIC
+*/
+
+const store = new Store();
+
+const randomString = (length) => {
+ const chars =
'0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
+
+ let result = '';
+ for (let i = length; i > 0; --i) {
+ result += chars[Math.floor(Math.random() * chars.length)];
+ }
+
+ return result;
+};
+
+const gateways = [
+ {
+ "id": "mdcyber",
+ "name": "MD Cybershuttle",
+ "gateway": "https://cybershuttle.org",
+ "loginUrl":
`https://iam.scigap.org/auth/realms/testdrive/protocol/openid-connect/auth?response_type=code&client_id=pga&redirect_uri=csagent%3A%2F%2Flogin-callback&scope=openid&state=${randomString(15)}&kc_idp_hint=cilogon&idp_alias=cilogon`
+ },
+ {
+ "id": "aicyber",
+ "name": "AI Cybershuttle",
+ "gateway": "https://ai.cybershuttleadfasfd.org",
+ "loginUrl": `https://google.com`
+ }
+];
+
+log.info("Gateways: ", gateways);
+
+let CURRENT_GATEWAY = store.get('stored-gateway');
+
+log.info("CURRENT_GATEWAY (before): ", CURRENT_GATEWAY);
+
+if (typeof CURRENT_GATEWAY === "undefined" || CURRENT_GATEWAY === null) {
+ CURRENT_GATEWAY = "mdcyber";
+}
+
+log.info("CURRENT_GATEWAY (after): ", CURRENT_GATEWAY);
+
+
+BASE_URL = gateways.find(g => g.id === CURRENT_GATEWAY)?.gateway;
+
+log.info("BASE_URL: ", BASE_URL);
+
+ipcMain.on('get-all-gateways', (event) => {
+ log.info("Getting all gateways");
+ event.sender.send('got-gateways', gateways);
+});
+
+ipcMain.on('get-gateway', (event) => {
+ log.info("Getting the gateway");
+ event.sender.send('gateway-got', CURRENT_GATEWAY);
+});
+
+ipcMain.on('set-gateway', (event, gateway) => {
+ log.info("Setting the gateway: ", gateway);
+ CURRENT_GATEWAY = gateway;
+ BASE_URL = gateways.find(g => g.id === CURRENT_GATEWAY).gateway;
+ store.set('stored-gateway', gateway);
+ event.sender.send('gateway-set', gateway);
+});
+
diff --git a/airavata-local-agent/package.json
b/airavata-local-agent/package.json
index ae275c2b55..63fde5fdce 100644
--- a/airavata-local-agent/package.json
+++ b/airavata-local-agent/package.json
@@ -2,7 +2,7 @@
"private": true,
"name": "cybershuttle-local-agent",
"description": "User interface for Apache Airavata.",
- "version": "2.0.6",
+ "version": "2.1.0",
"author": "Ganning Xu",
"main": "app/background.js",
"scripts": {
diff --git a/airavata-local-agent/renderer/components/HeaderBox.jsx
b/airavata-local-agent/renderer/components/HeaderBox.jsx
index d4915feef9..54d7a36565 100644
--- a/airavata-local-agent/renderer/components/HeaderBox.jsx
+++ b/airavata-local-agent/renderer/components/HeaderBox.jsx
@@ -23,7 +23,7 @@ import { Box, Flex, Spacer, Text, Modal, ModalOverlay,
ModalCloseButton, ModalHe
import { UserModal } from './UserModal';
import { useEffect, useState } from "react";
-export const HeaderBox = ({ name, email }) => {
+export const HeaderBox = ({ name, email, gatewayName }) => {
const { isOpen, onOpen, onClose } = useDisclosure();
const [version, setVersion] = useState('');
const [userObj, setUserObj] = useState({
@@ -100,7 +100,7 @@ export const HeaderBox = ({ name, email }) => {
onOpen();
}}
>
- {name} ({email}),
+ {name} ({email}, {gatewayName})
</Text>
</Tooltip>
{" "}
diff --git a/airavata-local-agent/renderer/lib/Contexts.js
b/airavata-local-agent/renderer/lib/Contexts.js
index 12ceb52008..268f275329 100644
--- a/airavata-local-agent/renderer/lib/Contexts.js
+++ b/airavata-local-agent/renderer/lib/Contexts.js
@@ -49,53 +49,62 @@ export const useAuth = () => {
return state;
};
+// Create context
+const BackendUrlContext = createContext();
-// import { useContext, createContext } from "react";
-
-// const TemplateContext = createContext({});
-
-// // Template Provider
-// const TemplateProvider = ({ children }) => {
-// const [accessToken, setAccessToken] = React.useState('');
-
-// // Context values passed to consumer
-// const value = {
-// accessToken, // <------ Expose Value to Consumer
-// setAccessToken // <------ Expose Setter to Consumer
-// };
-
-// return (
-// <TemplateContext.Provider value={value}>
-// {children}
-// </TemplateContext.Provider>
-// );
-// };
-
-// // Template Consumer
-// const TemplateConsumer = ({ children }) => {
-// return (
-// <TemplateContext.Consumer>
-// {(context) => {
-// if (context === undefined) {
-// throw new Error('TemplateConsumer must be used within
TemplateProvider');
-// }
-// return children(context);
-// }}
-// </TemplateContext.Consumer>
-// );
-// };
-
-// // useTemplate Hook
-// const useAuth = () => {
-// const context = useContext(AuthContext);
-// if (context === undefined) {
-// throw new Error('useTemplate must be used within TemplateProvider');
-// }
-// return context;
-// };
-
-// export {
-// TemplateProvider,
-// TemplateConsumer,
-// useAuth
-// };
+export const BackendUrlProvider = ({ children }) => {
+ const [gateway, setGateway] = useState('mdcyber');
+ const [allGateways, setAllGateways] = useState([]);
+
+ const [loading, setLoading] = useState(true);
+
+ useEffect(() => {
+ // Fetch gateways
+ window.ipc.send('get-all-gateways');
+ window.ipc.send('get-gateway');
+
+ window.ipc.on('got-gateways', (all) => {
+ setAllGateways(all);
+ });
+
+ window.ipc.on('gateway-got', (gateway) => {
+ setGateway(gateway);
+ setLoading(false);
+ });
+
+ return () => {
+ window.ipc.removeAllListeners('got-gateways');
+ window.ipc.removeAllListeners('gateway-got');
+ };
+ }, []);
+
+
+
+ // the user might want to change their gateway in the app too
+ const setGatewayId = (gateway) => {
+ setGateway(gateway);
+ window.ipc.send('set-gateway', gateway);
+ };
+
+
+ if (loading) {
+ return <div>Loading...</div>;
+ }
+ const ourGateway = allGateways?.find(g => g.id === gateway);
+
+ return (
+ <BackendUrlContext.Provider value={{
+ apiUrl: ourGateway?.gateway + '/api',
+ authUrl: ourGateway?.gateway + '/auth',
+ loginUrl: ourGateway?.loginUrl,
+ gatewayName: ourGateway?.name,
+ gateway,
+ allGateways,
+ setGatewayId
+ }}>
+ {children}
+ </BackendUrlContext.Provider>
+ );
+};
+
+export const useBackendUrls = () => useContext(BackendUrlContext);
diff --git a/airavata-local-agent/renderer/pages/_app.jsx
b/airavata-local-agent/renderer/pages/_app.jsx
index 55d8766b2e..8bfaf580fe 100644
--- a/airavata-local-agent/renderer/pages/_app.jsx
+++ b/airavata-local-agent/renderer/pages/_app.jsx
@@ -22,13 +22,15 @@
// pages/_app.jsx
import { useState } from 'react';
import { ChakraProvider } from '@chakra-ui/react';
-import { AuthContext, AuthProvider } from '../lib/Contexts';
+import { AuthContext, AuthProvider, BackendUrlProvider } from
'../lib/Contexts';
function MyApp({ Component, pageProps }) {
return (
<ChakraProvider>
<AuthProvider>
- <Component {...pageProps} />
+ <BackendUrlProvider>
+ <Component {...pageProps} />
+ </BackendUrlProvider>
</AuthProvider>
</ChakraProvider>
);
diff --git a/airavata-local-agent/renderer/pages/docker-home.jsx
b/airavata-local-agent/renderer/pages/docker-home.jsx
index 256f3a737f..fd86aeec33 100644
--- a/airavata-local-agent/renderer/pages/docker-home.jsx
+++ b/airavata-local-agent/renderer/pages/docker-home.jsx
@@ -29,6 +29,7 @@ import { AiOutlineCode } from "react-icons/ai";
import { useInterval } from "usehooks-ts";
import { DEBUG_DOCKER_MODE, API_BASE_URL, AUTH_BASE_URL, TOKEN_FILE } from
"../lib/constants";
import { motion } from 'framer-motion';
+import { useBackendUrls } from "../lib/Contexts";
const PING_DOCKER_INTERVAL = 10000;
@@ -73,6 +74,7 @@ const DockerHome = () => {
const [error, setError] = useState(null);
const [tabIndex, setTabIndex] = useState(1);
const toast = useToast();
+ const { gatewayName } = useBackendUrls();
useEffect(() => {
pingDocker();
@@ -142,7 +144,7 @@ const DockerHome = () => {
return (
<Box h='100vh' overflow='hidden' bg='gray.100'>
- <HeaderBox />
+ <HeaderBox gatewayName={gatewayName} />
<Tabs h='100%' index={tabIndex} onChange={(index) => setTabIndex(index)}
isLazy>
<Grid templateColumns='repeat(20, 1fr)' h='inherit'>
diff --git a/airavata-local-agent/renderer/pages/login.jsx
b/airavata-local-agent/renderer/pages/login.jsx
index c699674db2..2a3e4721ac 100644
--- a/airavata-local-agent/renderer/pages/login.jsx
+++ b/airavata-local-agent/renderer/pages/login.jsx
@@ -19,10 +19,10 @@
*
*
*****************************************************************/
-import { Box, Center, Flex, FormControl, FormLabel, Input, Img, Text, VStack,
Button, Alert, AlertIcon, Link, Heading, Spinner } from "@chakra-ui/react";
-import { useContext, useEffect, useState } from "react";
+import { Box, Center, Flex, Select, Img, Text, Button, Alert, AlertIcon, Link,
Heading, Spinner } from "@chakra-ui/react";
+import { useEffect, useState } from "react";
import { HeaderBox } from "../components/HeaderBox";
-import { AuthContext, useAuth } from "../lib/Contexts";
+import { useAuth, useBackendUrls } from "../lib/Contexts";
import { useRouter } from "next/router";
import { TOKEN_FILE } from "../lib/constants";
@@ -33,8 +33,11 @@ const Login = () => {
const [loading, setLoading] = useState(false);
const [verifyURL, setVerifyURL] = useState("");
const [isProd, setIsProd] = useState(false);
+ const [gatewayOptions, setGatewayOptions] = useState([]);
+ const [selectedGateway, setSelectedGateway] = useState("");
const [authInfo, setAuthInfo] = useAuth();
const router = useRouter();
+ const { apiUrl, authUrl, loginUrl, setGatewayId } = useBackendUrls();
// general a random string
const randomString = (length) => {
@@ -72,6 +75,8 @@ const Login = () => {
useEffect(() => {
window.ipc.send('is-prod');
+ window.ipc.send('get-all-gateways');
+ window.ipc.send('get-gateway');
if (localStorage.getItem("ciLoginAuto") === "true") {
setLoading(true);
@@ -126,10 +131,23 @@ const Login = () => {
setIsProd(isProdResult);
});
+
+ window.ipc.on('got-gateways', (data) => {
+ setGatewayOptions(data);
+ });
+
+ window.ipc.on('gateway-got', (gateway) => {
+ setSelectedGateway(gateway);
+ });
+
+ // TODO: ask background process for what gateway we are currently
configured on
+
return () => {
window.ipc.removeAllListeners('file-written');
window.ipc.removeAllListeners('file-read');
window.ipc.removeAllListeners('is-prod-reply');
+ window.ipc.removeAllListeners('got-gateways');
+ window.ipc.removeAllListeners('gateway-got');
};
}, []);
@@ -165,6 +183,34 @@ const Login = () => {
)
}
+ <Box shadow='md' rounded='md' p={4} mt={4}>
+ <Heading size='md' textAlign="left" color='blue.500'>
+ Gateway
+ </Heading>
+ <Select mt={4}
+ onChange={(e) => {
+ const gateway = e.target.value;
+
+ if (gateway) {
+ window.ipc.send('set-gateway', gateway);
+ setSelectedGateway(gateway);
+ setGatewayId(gateway);
+ }
+ }}
+ value={selectedGateway}
+ >
+ {
+ gatewayOptions.map((item) => {
+ return (
+ <option key={item.gateway} value={item.id}>
+ {item.name} ({item.gateway})
+ </option>
+ );
+ })
+ }
+ </Select>
+ </Box>
+
<Box shadow='md' rounded='md' p={4} mt={4}>
<Heading size='md' textAlign="left" color='blue.500'>
@@ -173,10 +219,9 @@ const Login = () => {
{
isProd ? (
<Button colorScheme='blue' w='full' mt={4}
-
onClick={() => {
setLoading(true);
-
window.open(`https://iam.scigap.org/auth/realms/testdrive/protocol/openid-connect/auth?response_type=code&client_id=pga&redirect_uri=csagent%3A%2F%2Flogin-callback&scope=openid&state=${randomString(15)}&kc_idp_hint=cilogon&idp_alias=cilogon`,
'_blank');
+ window.open(loginUrl, '_blank');
}}
isDisabled={loading}
> {