Fixed NameError when custom_handlers.py imports from jupyterhub_config.py
by protecting all c.* assignments with 'if c is not None:' checks.
Changes:
- Wrapped c = get_config() in try-except to handle module import context
- Defined DOCKER_SPAWNER_VOLUMES as module-level constant
- Modified get_user_volume_suffixes() to accept volumes dict parameter
- USER_VOLUME_SUFFIXES calculated from constant (importable without c defined)
- Protected all c.* configuration assignments with 'if c is not None:' guards
This allows custom_handlers.py to import USER_VOLUME_SUFFIXES without
triggering "NameError: name 'get_config' is not defined" when the config
file is imported as a module rather than loaded by JupyterHub.
Volume management now works correctly - configuration can be safely imported
by handlers to validate volume names against USER_VOLUME_SUFFIXES.
Updated volume management to dynamically read volume names from
DockerSpawner.volumes configuration instead of hardcoding them:
- Added get_user_volume_suffixes() function to extract volume suffixes from
config matching jupyterlab-{username}_<suffix> pattern
- Exposed USER_VOLUME_SUFFIXES via c.JupyterHub.template_vars for templates
- Updated home.html to use Jinja2 loop generating checkboxes dynamically
from user_volume_suffixes variable
- Modified ManageVolumesHandler to validate against configured volumes
instead of hardcoded {'home', 'workspace', 'cache'}
- Removed volume descriptions (e.g., "Contains: User home directory...") to
keep UI truly agnostic
Now works correctly if volumes are renamed, added, or removed in configuration
without requiring template or handler changes. Volume names displayed exactly
as defined in config (home, workspace, cache by default).
Implemented comprehensive notification broadcast functionality allowing
administrators to send notifications to all active user JupyterLab servers
simultaneously through a dedicated admin panel.
Core Features:
- Admin-only notification panel accessible at /hub/notifications
- Concurrent delivery to all active servers using asyncio with 5s timeout
- Temporary API token generation (5-minute expiry) for authentication
- Support for 6 notification types: default, info, success, warning, error, in-progress
- 140-character message limit with live character counter
- Auto-close toggle and dismiss button in notifications
- Dynamic endpoint URL construction using spawner.server.base_url
- Comprehensive error handling with user-friendly messages
- One-line logging per server with message preview and outcome
Technical Implementation:
- Created BroadcastNotificationHandler in custom_handlers.py
- Created NotificationsPageHandler for admin UI rendering
- Added notifications.html template with Bootstrap 5 form
- Registered handlers in jupyterhub_config.py extra_handlers
- Sends to /jupyterlab-notifications-extension/ingest endpoint
- Payload includes type, message, autoClose, and actions array
- Navigation link added to home.html for admin access
Integration:
- Requires jupyterlab_notifications_extension installed on JupyterLab servers
- Uses correct payload format (type field, not variant)
- Includes Dismiss action button for manual notification closure
Documentation:
- Updated .claude/CLAUDE.md with complete feature documentation
- Updated README.md Features section with notification broadcast details
- Updated .claude/JOURNAL.md with implementation summary
- Removed obsolete FEATURE_PLAN.md
Version: 3.2.0 (bumped from 3.1.2)
Implemented built-in protected group system enabling admins to grant trusted users read-write Docker socket access within their JupyterLab containers. Groups are managed through admin panel and cannot be permanently deleted.
- Add /srv/jupyterhub to sys.path before importing custom_handlers
- Separate shell scripts and Python files in Dockerfile COPY commands
- Resolves ModuleNotFoundError for custom_handlers module
- JupyterHub now starts successfully with custom API handlers
- Add custom API handlers for volume reset and server restart
- Create custom home.html template with self-service buttons and modals
- Register handlers in jupyterhub_config.py with @admin_or_self permissions
- Update Dockerfile to copy templates and handlers
- Add custom templates path to JupyterHub configuration
- Update .claude/CLAUDE.md with feature documentation
- Reset Home Volume: DELETE /hub/api/users/{username}/reset-home-volume
- Restart Server: POST /hub/api/users/{username}/restart-server
- Both features use Docker API directly via /var/run/docker.sock