Skip to content

Commit

Permalink
Fix disconnection bug and move assets (#663)
Browse files Browse the repository at this point in the history
* change to hypha-core

* upgrade hypha-core

* Add delay when closing the login window

* Fix disconnect issue

* move assets

* bump hypha rpc to 0.20.30
  • Loading branch information
oeway authored Aug 25, 2024
1 parent 2a1969c commit ba85af1
Show file tree
Hide file tree
Showing 17 changed files with 101 additions and 104 deletions.
2 changes: 1 addition & 1 deletion docs/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,7 @@ svc = await get_remote_service("http://localhost:9527/ws-user-scintillating-lawy
Include the following script in your HTML file to load the `hypha-rpc` client:

```html
<script src="https://cdn.jsdelivr.net/npm/[email protected].28/dist/hypha-rpc-websocket.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected].30/dist/hypha-rpc-websocket.min.js"></script>
```

Use the following code in JavaScript to connect to the server and access an existing service:
Expand Down
10 changes: 5 additions & 5 deletions docs/migration-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ To connect to the server, instead of installing the `imjoy-rpc` module, you will
pip install -U hypha-rpc # new install
```

We also changed our versioning strategy, we use the same version number for the server and client, so it's easier to match the client and server versions. For example, `hypha-rpc` version `0.20.28` is compatible with Hypha server version `0.20.28`.
We also changed our versioning strategy, we use the same version number for the server and client, so it's easier to match the client and server versions. For example, `hypha-rpc` version `0.20.30` is compatible with Hypha server version `0.20.30`.

#### 2. Change the imports to use `hypha-rpc`

Expand Down Expand Up @@ -128,10 +128,10 @@ loop.run_forever()
To connect to the server, instead of using the `imjoy-rpc` module, you will need to use the `hypha-rpc` module. The `hypha-rpc` module is a standalone module that provides the RPC connection to the Hypha server. You can include it in your HTML using a script tag:

```html
<script src="https://cdn.jsdelivr.net/npm/[email protected].28/dist/hypha-rpc-websocket.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected].30/dist/hypha-rpc-websocket.min.js"></script>
```

We also changed our versioning strategy, we use the same version number for the server and client, so it's easier to match the client and server versions. For example, `hypha-rpc` version `0.20.28` is compatible with Hypha server version `0.20.28`.
We also changed our versioning strategy, we use the same version number for the server and client, so it's easier to match the client and server versions. For example, `hypha-rpc` version `0.20.30` is compatible with Hypha server version `0.20.30`.

#### 2. Change the connection method and use camelCase for service function names

Expand All @@ -149,7 +149,7 @@ Here is a suggested list of search and replace operations to update your code:
Here is an example of how the updated code might look:

```html
<script src="https://cdn.jsdelivr.net/npm/[email protected].28/dist/hypha-rpc-websocket.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected].30/dist/hypha-rpc-websocket.min.js"></script>
<script>
async function main(){
const server = await hyphaWebsocketClient.connectToServer({"server_url": "https://hypha.amun.ai"});
Expand Down Expand Up @@ -197,7 +197,7 @@ We created a tutorial to introduce this new feature: [service type annotation](.
Here is a quick example in JavaScript:

```html
<script src="https://cdn.jsdelivr.net/npm/[email protected].28/dist/hypha-rpc-websocket.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected].30/dist/hypha-rpc-websocket.min.js"></script>

<script>
async function main(){
Expand Down
2 changes: 1 addition & 1 deletion docs/service-type-annotation.md
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ if __name__ == "__main__":
**JavaScript Client: Service Usage**

```html
<script src="https://cdn.jsdelivr.net/npm/[email protected].28/dist/hypha-rpc-websocket.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected].30/dist/hypha-rpc-websocket.min.js"></script>
<script>
async function main() {
const server = await hyphaWebsocketClient.connectToServer({"server_url": "https://hypha.amun.ai"});
Expand Down
2 changes: 1 addition & 1 deletion helm-chart/hypha-server/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ image:
repository: ghcr.io/amun-ai/hypha
pullPolicy: IfNotPresent
# Overrides the image tag whose default is the chart appVersion.
tag: "0.20.28"
tag: "0.20.30"

imagePullSecrets: []
nameOverride: ""
Expand Down
2 changes: 1 addition & 1 deletion hypha/VERSION
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
"version": "0.20.28"
"version": "0.20.30"
}
4 changes: 2 additions & 2 deletions hypha/apps.py
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,7 @@ async def install(
source = temp.render(
hypha_main_version=main_version,
hypha_rpc_websocket_mjs=self.public_base_url
+ "/hypha-rpc-websocket.mjs",
+ "/assets/hypha-rpc-websocket.mjs",
config={k: config[k] for k in config if k in PLUGIN_CONFIG_FIELDS},
script=config["script"],
requirements=config["requirements"],
Expand Down Expand Up @@ -304,7 +304,7 @@ async def install(
source = temp.render(
hypha_main_version=main_version,
hypha_rpc_websocket_mjs=self.public_base_url
+ "/hypha-rpc-websocket.mjs",
+ "//assets/hypha-rpc-websocket.mjs",
script=source,
source_hash=mhash,
config=config,
Expand Down
38 changes: 20 additions & 18 deletions hypha/core/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -403,14 +403,13 @@ async def disconnect(self, reason=None):
class RedisEventBus:
"""Represent a redis event bus."""

def __init__(self, redis, logger=None) -> None:
def __init__(self, redis) -> None:
"""Initialize the event bus."""
self._redis = redis
self._handle_connected = None
self._stop = False
self._local_event_bus = EventBus(logger)
self._redis_event_bus = EventBus(logger)
self._logger = logger

async def init(self):
"""Setup the event bus."""
Expand Down Expand Up @@ -529,22 +528,25 @@ async def _subscribe_redis(self):
self._ready.set_result(True)
while self._stop is False:
msg = await pubsub.get_message(ignore_subscribe_messages=True)
if msg:
channel = msg["channel"].decode("utf-8")
if channel.startswith("event:b:"):
event_type = channel[8:]
data = msg["data"]
await self._redis_event_bus.emit(event_type, data)
elif channel.startswith("event:d:"):
event_type = channel[8:]
data = json.loads(msg["data"])
await self._redis_event_bus.emit(event_type, data)
elif channel.startswith("event:s:"):
event_type = channel[8:]
data = msg["data"].decode("utf-8")
await self._redis_event_bus.emit(event_type, data)
else:
self._logger.info("Unknown channel: %s", channel)
try:
if msg:
channel = msg["channel"].decode("utf-8")
if channel.startswith("event:b:"):
event_type = channel[8:]
data = msg["data"]
await self._redis_event_bus.emit(event_type, data)
elif channel.startswith("event:d:"):
event_type = channel[8:]
data = json.loads(msg["data"])
await self._redis_event_bus.emit(event_type, data)
elif channel.startswith("event:s:"):
event_type = channel[8:]
data = msg["data"].decode("utf-8")
await self._redis_event_bus.emit(event_type, data)
else:
logger.info("Unknown channel: %s", channel)
except Exception as exp:
logger.exception(f"Error processing message: {exp}")
await asyncio.sleep(0.01)
except Exception as exp:
self._ready.set_exception(exp)
41 changes: 4 additions & 37 deletions hypha/http.py
Original file line number Diff line number Diff line change
Expand Up @@ -197,17 +197,7 @@ def norm_url(url):
self.rpc_lib_esm_content = None
self.rpc_lib_umd_content = None

@router.get(norm_url("/authorize"))
async def auth_proxy(request: Request):
# Construct the full URL for the Auth0 authorize endpoint with the query parameters
auth0_authorize_url = (
f"https://{AUTH0_DOMAIN}/authorize?{request.query_params}"
)

# Redirect the client to the constructed URL
return RedirectResponse(url=auth0_authorize_url)

@router.get(norm_url("/hypha-rpc-websocket.mjs"))
@router.get(norm_url("/assets/hypha-rpc-websocket.mjs"))
async def hypha_rpc_websocket_mjs(request: Request):
if not self.rpc_lib_esm_content:
_rpc_lib_script = f"https://cdn.jsdelivr.net/npm/hypha-rpc@{main_version}/dist/hypha-rpc-websocket.mjs"
Expand All @@ -218,7 +208,7 @@ async def hypha_rpc_websocket_mjs(request: Request):
content=self.rpc_lib_esm_content, media_type="application/javascript"
)

@router.get(norm_url("/hypha-rpc-websocket.js"))
@router.get(norm_url("/assets/hypha-rpc-websocket.js"))
async def hypha_rpc_websocket_js(request: Request):
if not self.rpc_lib_umd_content:
_rpc_lib_script = f"https://cdn.jsdelivr.net/npm/hypha-rpc@{main_version}/dist/hypha-rpc-websocket.js"
Expand All @@ -229,7 +219,7 @@ async def hypha_rpc_websocket_js(request: Request):
content=self.rpc_lib_umd_content, media_type="application/javascript"
)

@router.get(norm_url("/config.json"))
@router.get(norm_url("/assets/config.json"))
async def get_config(
request: Request,
user_info: store.login_optional = Depends(store.login_optional),
Expand All @@ -251,7 +241,7 @@ async def token_proxy(request: Request):

return JSONResponse(status_code=200, content=auth0_response.json())

@router.get(norm_url("/authorize"))
@router.get(norm_url("/oauth/authorize"))
async def auth_proxy(request: Request):
# Construct the full URL for the Auth0 authorize endpoint with the query parameters
auth0_authorize_url = (
Expand All @@ -261,18 +251,6 @@ async def auth_proxy(request: Request):
# Redirect the client to the constructed URL
return RedirectResponse(url=auth0_authorize_url)

@router.post(norm_url("/oauth/token"))
async def token_proxy(request: Request):
form_data = await request.form()
async with httpx.AsyncClient() as client:
auth0_response = await client.post(
f"https://{AUTH0_DOMAIN}/oauth/token",
data=form_data,
headers={"Content-Type": "application/x-www-form-urlencoded"},
)

return JSONResponse(status_code=200, content=auth0_response.json())

@router.get(norm_url("/{workspace}/info"))
async def get_workspace_info(
workspace: str,
Expand Down Expand Up @@ -750,17 +728,6 @@ async def get_pages(
page = "index.html"
dir_path = page.split("/")[0]
if dir_path not in self.templates_files and "-" in dir_path:
workspace = dir_path
try:
await self.store.load_or_create_workspace(user_info, workspace)
except KeyError:
return JSONResponse(
status_code=404,
content={
"success": False,
"detail": f"Workspace not found: {workspace}",
},
)
inner_path = "/".join(page.split("/")[1:])
if not inner_path:
inner_path = "index.html"
Expand Down
2 changes: 1 addition & 1 deletion hypha/templates/apps/imjoy-plugin-parser.html
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
];
const config = Object.fromEntries(new URLSearchParams(window.location.search))
if(!config.server_url) config.server_url = window.location.origin;
const hyphaWebsocketClient = await import(config.server_url + '/hypha-rpc-websocket.mjs');
const hyphaWebsocketClient = await import(config.server_url + '/assets/hypha-rpc-websocket.mjs');
const api = await hyphaWebsocketClient.connectToServer(config)
const pluginParser = await import("https://cdn.jsdelivr.net/npm/[email protected]/src/pluginParser.js")
function parsePluginCode(code, overwrite_config) {
Expand Down
35 changes: 28 additions & 7 deletions hypha/templates/apps/login_template.html
Original file line number Diff line number Diff line change
Expand Up @@ -181,25 +181,46 @@
if (key && report_url.startsWith('http')) {
const url = `${report_url}?key=${key}&token=${accessToken}&email=${user.email}&email_verified=${user.email_verified}&name=${user.name}&nickname=${user.nickname}&user_id=${user.sub}&picture=${user.picture}`;
const response = await fetch(url);
if (response.ok) {
window.close();
} else {
if (!response.ok) {
console.error("Failed to report token: ", response.status, response.statusText);
alert(`Failed to report token. Please try again later. (${response.status}: ${response.statusText})`);
window.close();
}
setTimeout(() => {
window.close();
}, 500);
}
};

window.logout = async () => {
const href = window.location.href;
let href = window.location.href;
if(!href.endsWith("/")){
href += "/";
}
await auth0.logout({ logoutParams: { returnTo: href } });
};

window.login = async () => {
try {
await auth0.loginWithPopup({ audience: AUTH0_AUDIENCE });
await showUserProfile();
// Manually open the popup window
const popupWindow = window.open('', '_blank', 'width=500,height=600');

if (popupWindow) {
try {
// Pass the opened popup window to the Auth0 loginWithPopup method
await auth0.loginWithPopup({ audience: AUTH0_AUDIENCE }, { popup: popupWindow });
popupWindow.close(); // Close the popup window after login
await showUserProfile();
} catch (e) {
console.error('Login with popup failed', e);
if (!popupWindow.closed) {
popupWindow.close(); // Ensure the popup window is closed in case of failure
}
}
} else {
console.error('Unable to open popup window for login');
alert('It seesm popup window is blocked by the browser. Please allow popups for the login');
}

} catch (e) {
alert("Failed to open login window. Please allow pop-ups for this site.");
console.error(e);
Expand Down
2 changes: 1 addition & 1 deletion hypha/templates/apps/window.index.html
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
const config = {{ config | tojson(indent=2) }}
const cfg = Object.assign(config, Object.fromEntries(new URLSearchParams(window.location.search)));
if(!cfg.server_url) cfg.server_url = window.location.origin;
const hyphaWebsocketClient = await import(cfg.server_url + '/hypha-rpc-websocket.mjs');
const hyphaWebsocketClient = await import(cfg.server_url + '/assets/hypha-rpc-websocket.mjs');
hyphaWebsocketClient.connectToServer(cfg).then(async (api)=>{
await hyphaWebsocketClient.loadRequirements([{% for req in requirements %}"{{req}}", {% endfor %}])
{{ script | safe }}
Expand Down
2 changes: 1 addition & 1 deletion hypha/templates/hypha-core-app/hypha-app-webpython.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ loadPyodide().then(async (pyodide) => {
pyodide.setStderr({ batched: (msg) => console.error(msg) });
await pyodide.loadPackage("micropip");
const micropip = pyodide.pyimport("micropip");
await micropip.install('hypha-rpc==0.20.28');
await micropip.install('hypha-rpc==0.20.30');
const isWindow = typeof window !== "undefined";

setTimeout(() => {
Expand Down
31 changes: 24 additions & 7 deletions hypha/templates/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@
<img src="./static/img/hypha-logo-black.svg" className="h-10 invert" alt="Hypha Logo" />
</div>
<div className="flex space-x-6">
<a href="#" className="hover:text-gray-400 transition duration-300">Dashboard</a>
<a href="https://desktop.aicell.io" className="hover:text-gray-400 transition duration-300">Desktop</a>
<a href="#" className="hover:text-gray-400 transition duration-300">Marketplace</a>
<a href="https://docs.amun.ai/" target="_blank" className="hover:text-gray-400 transition duration-300">Documentation</a>
<a href="#" className="hover:text-gray-400 transition duration-300" onClick={onAboutClick}>About</a>
Expand All @@ -90,7 +90,6 @@
</button>
<div className="dropdown-content">
<a href="#">Signed in as {user.name}</a>
<a href="https://desktop.aicell.io">Hypha Desktop</a>
<a href={"./ws-user-" + user.sub}>My Workspace</a>
<a href="#" onClick={onLogout}>Logout</a>
</div>
Expand Down Expand Up @@ -165,7 +164,7 @@ <h3 className="text-xl font-bold">{title}</h3>

React.useEffect(() => {
const fetchConfig = async () => {
const response = await fetch('./config.json');
const response = await fetch('./assets/config.json');
const configData = await response.json();
setConfig(configData);

Expand Down Expand Up @@ -213,11 +212,29 @@ <h3 className="text-xl font-bold">{title}</h3>

const onLogin = async () => {
try {
await window.auth0Client.loginWithPopup();
const userProfile = await window.auth0Client.getUser();
setUser(userProfile);
setIsLoggedIn(true);

// Manually open the popup window
const popupWindow = window.open('', '_blank', 'width=500,height=600');
if (popupWindow) {
try {
// Pass the opened popup window to the Auth0 loginWithPopup method
await window.auth0Client.loginWithPopup({ audience: config.auth0_audience }, { popup: popupWindow });
popupWindow.close(); // Close the popup window after login
const userProfile = await window.auth0Client.getUser();
setUser(userProfile);
setIsLoggedIn(true);
} catch (e) {
console.error('Login with popup failed', e);
if (!popupWindow.closed) {
popupWindow.close(); // Ensure the popup window is closed in case of failure
}
}
} else {
console.error('Unable to open popup window for login');
alert('It seesm popup window is blocked by the browser. Please allow popups for the login');
}
} catch (e) {
alert("Failed to open login window. Please allow pop-ups for this site.");
console.error(e);
}
};
Expand Down
Loading

0 comments on commit ba85af1

Please sign in to comment.