diff --git a/binderhub/launcher.py b/binderhub/launcher.py index 8d7b81fbe..a6f0cd423 100644 --- a/binderhub/launcher.py +++ b/binderhub/launcher.py @@ -148,6 +148,17 @@ async def get_user_data(self, username): body = json.loads(resp.body.decode("utf-8")) return body + async def get_named_server_count(self, username): + """Return the number of named servers for a given JupyterHub user.""" + try: + user_data = await self.get_user_data(username) + except RuntimeError: + return 0 + servers = user_data.get("servers", {}) + if not isinstance(servers, dict): + return 0 + return sum(1 for server_name in servers if server_name != "") + def unique_name_from_repo(self, repo_url): """Generate a unique name for a git repo url diff --git a/binderhub/main.py b/binderhub/main.py index 357e2bef4..570075b79 100644 --- a/binderhub/main.py +++ b/binderhub/main.py @@ -23,17 +23,32 @@ def initialize(self): return super().initialize() @authenticated - def get(self): + async def get(self): repoproviders_display_config = [ repo_provider_class.display_config for repo_provider_class in self.settings["repo_providers"].values() ] + running_environment_count = None + launcher = self.settings.get("launcher") + if launcher is not None: + try: + user = self.get_current_user() + username = user.get("name") if isinstance(user, dict) else user + if username: + running_environment_count = await launcher.get_named_server_count( + username + ) + except Exception: + self.log.debug( + "Unable to determine current environment count", exc_info=True + ) self.page_config |= { "baseUrl": self.settings["base_url"], "badgeBaseUrl": self.get_badge_base_url(), "logoUrl": self.static_url("logo.svg"), "logoWidth": "320px", "repoProviders": repoproviders_display_config, + "runningEnvironmentCount": running_environment_count, "aboutMessage": self.settings["about_message"], "bannerHtml": self.settings["banner_message"], "binderVersion": binder_version, @@ -58,7 +73,7 @@ def initialize(self, repo_provider): return super().initialize() @authenticated - def get(self, provider_id, _escaped_spec): + async def get(self, provider_id, _escaped_spec): prefix = "/v2/" + provider_id spec = self.get_spec_from_request(prefix) @@ -75,7 +90,7 @@ def get(self, provider_id, _escaped_spec): self.opengraph_title = ( f"{self.repo_provider.display_config['displayName']}: {spec}" ) - return super().get() + return await super().get() class LegacyRedirectHandler(BaseHandler): diff --git a/binderhub/static/js/components/LinkGenerator.jsx b/binderhub/static/js/components/LinkGenerator.jsx index 7492ee08d..439b03587 100644 --- a/binderhub/static/js/components/LinkGenerator.jsx +++ b/binderhub/static/js/components/LinkGenerator.jsx @@ -1,5 +1,6 @@ import { useEffect, useState } from "react"; import copy from "copy-to-clipboard"; +import { PAGE_CONFIG } from "../App.jsx"; /** * @typedef {object} ProviderSelectorProps @@ -346,6 +347,17 @@ export function LinkGenerator({ →問い合わせ先/エラーが発生したときは
Contact Information/Troubleshooting + {PAGE_CONFIG.runningEnvironmentCount !== undefined && + PAGE_CONFIG.runningEnvironmentCount !== null && ( +
+ + あなたは {PAGE_CONFIG.runningEnvironmentCount}個の解析環境を持っています + + {PAGE_CONFIG.runningEnvironmentCount >= 10 && ( + <>
解析環境の数が上限に達しています + )} +
+ )} ); } diff --git a/helm-chart/binderhub/Chart.yaml b/helm-chart/binderhub/Chart.yaml index a6fe67e0c..ba1f13453 100644 --- a/helm-chart/binderhub/Chart.yaml +++ b/helm-chart/binderhub/Chart.yaml @@ -1,7 +1,7 @@ # Chart.yaml v2 reference: https://helm.sh/docs/topics/charts/#the-chartyaml-file apiVersion: v2 name: binderhub -version: 0.2.5-20260304.3 +version: 0.2.5-20260827 dependencies: # Source code: https://github.com/jupyterhub/zero-to-jupyterhub-k8s # Latest version: https://github.com/jupyterhub/zero-to-jupyterhub-k8s/tags diff --git a/integration-tests/test_ui.py b/integration-tests/test_ui.py index 4b0215098..36c5a67d5 100644 --- a/integration-tests/test_ui.py +++ b/integration-tests/test_ui.py @@ -35,7 +35,7 @@ async def local_hub_local_binder(request): ) url = f"http://127.0.0.1:{port}/services/binder/" - for i in range(10): + for i in range(30): try: resp = await async_requests.get(url) if resp.status_code == 200: