diff --git a/docs/source/_static/rest-api.yml b/docs/source/_static/rest-api.yml index 324e6d08..74b309b6 100644 --- a/docs/source/_static/rest-api.yml +++ b/docs/source/_static/rest-api.yml @@ -1176,8 +1176,16 @@ paths: example: abc123 accept_url: type: string - description: The URL for accepting the code + description: The URL path for accepting the code example: /hub/accept-share?code=abc123 + full_accept_url: + type: + - string + - "null" + description: | + The full URL for accepting the code, + if JupyterHub.public_url configuration is defined. + example: https://hub.example.org/hub/accept-share?code=abc123 security: - oauth2: - shares @@ -1877,7 +1885,14 @@ components: description: the server name. '' for the default server. url: type: string - description: the server's URL + description: the server's URL (path only when not using subdomains) + full_url: + type: + - string + - "null" + description: | + The full URL of the server (`https://hub.example.org/user/:name/:servername`). + `null` unless JupyterHub.public_url or subdomains are configured. ready: type: boolean description: whether the server is ready diff --git a/docs/source/reference/sharing.md b/docs/source/reference/sharing.md index f725a145..0ea589ac 100644 --- a/docs/source/reference/sharing.md +++ b/docs/source/reference/sharing.md @@ -286,6 +286,7 @@ The response contains the code itself: { "code": "abc1234....", "accept_url": "/hub/accept-share?code=abc1234", + "full_accept_url": "https://hub.example.org/hub/accept-share?code=abc1234", "id": "sc_1234", "scopes": [...], ... diff --git a/docs/source/tutorial/sharing.md b/docs/source/tutorial/sharing.md index 22e1705e..9612f581 100644 --- a/docs/source/tutorial/sharing.md +++ b/docs/source/tutorial/sharing.md @@ -159,11 +159,14 @@ which will have a JSON response: 'last_exchanged_at': None, 'code': 'U-eYLFT1lGstEqfMHpAIvTZ1MRjZ1Y1a-loGQ0K86to', 'accept_url': '/hub/accept-share?code=U-eYLFT1lGstEqfMHpAIvTZ1MRjZ1Y1a-loGQ0K86to', + 'full_accept_url': 'https://hub.example.org/accept-share?code=U-eYLFT1lGstEqfMHpAIvTZ1MRjZ1Y1a-loGQ0K86to', } ``` The most relevant fields here are `code`, which contains the code itself, and `accept_url`, which is the URL path for the page another user. Note: it does not contain the _hostname_ of the hub, which JupyterHub often does not know. +If `public_url` configuration is defined, `full_accept_url` will be the full URL including the host. +Otherwise, it will be null. Share codes are guaranteed to be url-safe, so no encoding is required. diff --git a/jupyterhub/apihandlers/shares.py b/jupyterhub/apihandlers/shares.py index 7ff384fb..68166fde 100644 --- a/jupyterhub/apihandlers/shares.py +++ b/jupyterhub/apihandlers/shares.py @@ -5,6 +5,7 @@ import json import re from typing import List, Optional +from urllib.parse import urlunparse from pydantic import ( BaseModel, @@ -95,7 +96,7 @@ class _ShareAPIHandler(APIHandler): } } # subset keys for sharing - for key in ["name", "url", "ready"]: + for key in ["name", "url", "full_url", "ready"]: if key in full_model: server_model[key] = full_model[key] @@ -128,6 +129,12 @@ class _ShareAPIHandler(APIHandler): model["accept_url"] = url_concat( self.hub.base_url + "accept-share", {"code": code} ) + model["full_accept_url"] = None + public_url = self.settings.get("public_url") + if public_url: + model["full_accept_url"] = urlunparse( + public_url._replace(path=model["accept_url"]) + ) return model def _init_share_query(self, kind="share"):