Repository: jupyterhub/jupyterhub

Issue:
Empty Admin page when base_url is set
### Bug description
<!-- Use this section to clearly and concisely describe the bug. -->

If `JupyterHub.base_url` is set in `jupyterhub_config.py`, visitig the Admin tab on jupyterhub v.2.0.0 shows the empty page. 

#### Expected behaviour
<!-- Tell us what you thought would happen. -->

Without setting  `JupyterHub.base_url` the Admin page displays the list of users:

<img width="857" alt="Screenshot 2021-12-10 at 17 41 29" src="https://user-images.githubusercontent.com/2140361/145609729-d5ee0c2c-bdb4-4e77-a730-ccb2ddbbc07a.png">

#### Actual behaviour
<!-- Tell us what actually happens. -->

The page shows only the version number:

<img width="767" alt="Screenshot 2021-12-10 at 17 29 16" src="https://user-images.githubusercontent.com/2140361/145608037-34be2e43-e02d-40a6-b643-79c4628d8915.png">

I see some errors in the log:

```
I 2021-12-10 16:25:47.787 JupyterHub log:189] 200 GET /test/hub/admin (user@::ffff:172.17.0.1) 42.95ms
16:25:48.067 [ConfigProxy] error: 404 GET /hub/api/users 
16:25:48.076 [ConfigProxy] error: 404 GET /hub/api/groups 
```

### How to reproduce
<!-- Use this section to describe the steps that a user would take to experience this bug. -->

1. Create `Dockerfile` with the following content:
```
FROM jupyterhub/jupyterhub:2.0.0

COPY jupyterhub_config.py /etc/jupyterhub/

CMD jupyterhub -f /etc/jupyterhub/jupyterhub_config.py
```

2. In the same directory create `jupyterhub_config.py`:
```
c.JupyterHub.base_url = '/test/'

c.Authenticator.allowed_users = {'user'}
c.Authenticator.admin_users = {'user'}

c.JupyterHub.authenticator_class = 'dummy'
c.JupyterHub.spawner_class = 'simple'
```
3. Compile docker image:
```
docker build -t jupyterhub-test .
```
4. Start the container:
```
docker run -it -p 8000:8000 jupyterhub-test
```
5. Open the page: http://127.0.0.1:8000/test/hub/admin
6. Enter the user name `user`


### Your personal set up
<!--
Tell us a little about the system you're using.
Please include information about how you installed,
e.g. are you using a distribution such as zero-to-jupyterhub or the-littlest-jupyterhub.
 -->

 - OS: MacOS 12.0.1
 - Version(s):
```
% docker --version
Docker version 20.10.11, build dea9396
```

<details><summary>Logs</summary>

```
% docker run -it -p 8000:8000 jupyterhub-test
[I 2021-12-10 16:49:35.000 JupyterHub app:2717] Running JupyterHub version 2.0.0
[I 2021-12-10 16:49:35.001 JupyterHub app:2747] Using Authenticator: jupyterhub.auth.DummyAuthenticator-2.0.0
[I 2021-12-10 16:49:35.003 JupyterHub app:2747] Using Spawner: jupyterhub.spawner.SimpleLocalProcessSpawner-2.0.0
[I 2021-12-10 16:49:35.004 JupyterHub app:2747] Using Proxy: jupyterhub.proxy.ConfigurableHTTPProxy-2.0.0
[I 2021-12-10 16:49:35.023 JupyterHub app:1641] Writing cookie_secret to /srv/jupyterhub/jupyterhub_cookie_secret
[I 2021-12-10 16:49:35.101 alembic.runtime.migration migration:201] Context impl SQLiteImpl.
[I 2021-12-10 16:49:35.101 alembic.runtime.migration migration:204] Will assume non-transactional DDL.
[I 2021-12-10 16:49:35.152 alembic.runtime.migration migration:615] Running stamp_revision  -> 833da8570507
[I 2021-12-10 16:49:35.485 JupyterHub proxy:496] Generating new CONFIGPROXY_AUTH_TOKEN
[I 2021-12-10 16:49:35.629 JupyterHub roles:358] Adding role user for User: user
[I 2021-12-10 16:49:35.650 JupyterHub roles:358] Adding role admin for User: user
[W 2021-12-10 16:49:35.656 JupyterHub app:2152] No admin role found; assuming hub upgrade. Initializing default roles for all entities
[I 2021-12-10 16:49:35.727 JupyterHub app:2786] Initialized 0 spawners in 0.008 seconds
[W 2021-12-10 16:49:35.739 JupyterHub proxy:687] Running JupyterHub without SSL.  I hope there is SSL termination happening somewhere else...
[I 2021-12-10 16:49:35.739 JupyterHub proxy:691] Starting proxy @ http://:8000/test/
16:49:36.527 [ConfigProxy] info: Proxying http://*:8000 to (no default)
16:49:36.536 [ConfigProxy] info: Proxy API at http://127.0.0.1:8001/api/routes
16:49:36.846 [ConfigProxy] info: 200 GET /api/routes 
[I 2021-12-10 16:49:36.847 JupyterHub app:3035] Hub API listening on http://127.0.0.1:8081/test/hub/
16:49:36.852 [ConfigProxy] info: 200 GET /api/routes 
[I 2021-12-10 16:49:36.857 JupyterHub proxy:431] Adding route for Hub: /test/ => http://127.0.0.1:8081
16:49:36.870 [ConfigProxy] info: Adding route /test -> http://127.0.0.1:8081
16:49:36.874 [ConfigProxy] info: Route added /test -> http://127.0.0.1:8081
16:49:36.875 [ConfigProxy] info: 201 POST /api/routes/test 
[I 2021-12-10 16:49:36.878 JupyterHub app:3101] JupyterHub is now running at http://:8000/test/
[W 2021-12-10 16:49:56.934 JupyterHub base:391] Invalid or expired cookie token
[I 2021-12-10 16:49:56.944 JupyterHub log:189] 302 GET /test/hub/admin -> /test/hub/login?next=%2Ftest%2Fhub%2Fadmin (@::ffff:172.17.0.1) 12.91ms
[I 2021-12-10 16:49:57.036 JupyterHub log:189] 200 GET /test/hub/login?next=%2Ftest%2Fhub%2Fadmin (@::ffff:172.17.0.1) 63.43ms
[I 2021-12-10 16:50:01.674 JupyterHub base:796] User logged in: user
[I 2021-12-10 16:50:01.677 JupyterHub log:189] 302 POST /test/hub/login?next=%2Ftest%2Fhub%2Fadmin -> /test/hub/admin (user@::ffff:172.17.0.1) 59.37ms
[I 2021-12-10 16:50:01.741 JupyterHub log:189] 200 GET /test/hub/admin (user@::ffff:172.17.0.1) 45.71ms
16:50:01.985 [ConfigProxy] error: 404 GET /hub/api/groups 
16:50:02.004 [ConfigProxy] error: 404 GET /hub/api/users 
[I 2021-12-10 16:50:02.043 JupyterHub log:189] 200 GET /test/hub/error/404?url=%2Fhub%2Fapi%2Fusers%3Foffset%3D0%26limit%3D50 (@127.0.0.1) 22.30ms
[I 2021-12-10 16:50:02.061 JupyterHub log:189] 200 GET /test/hub/error/404?url=%2Fhub%2Fapi%2Fgroups%3Foffset%3D0%26limit%3D50 (@127.0.0.1) 14.26ms
```
</details>



Discussion:
@naatebarber sounds like there might be an absolute URL assumption somewhere in the new admin js?
Yeah `base_url` isn't being passed, will create a branch.

Resolving patch:
diff --git a/jupyterhub/handlers/pages.py b/jupyterhub/handlers/pages.py
--- a/jupyterhub/handlers/pages.py
+++ b/jupyterhub/handlers/pages.py
@@ -465,6 +465,7 @@ async def get(self):
             named_server_limit_per_user=self.named_server_limit_per_user,
             server_version=f'{__version__} {self.version_hash}',
             api_page_limit=self.settings["api_page_default_limit"],
+            base_url=self.settings["base_url"],
         )
         self.finish(html)
 


Test patch:
diff --git a/jsx/src/components/ServerDashboard/ServerDashboard.test.js b/jsx/src/components/ServerDashboard/ServerDashboard.test.js
--- a/jsx/src/components/ServerDashboard/ServerDashboard.test.js
+++ b/jsx/src/components/ServerDashboard/ServerDashboard.test.js
@@ -33,11 +33,7 @@ var serverDashboardJsx = (spy) => (
 );
 
 var mockAsync = (data) =>
-  jest
-    .fn()
-    .mockImplementation(() =>
-      Promise.resolve({ json: () => Promise.resolve(data ? data : { k: "v" }) })
-    );
+  jest.fn().mockImplementation(() => Promise.resolve(data ? data : { k: "v" }));
 
 var mockAsyncRejection = () =>
   jest.fn().mockImplementation(() => Promise.reject());