DSP Project first push, date: 29/01/2026
This commit is contained in:
203
includes/jupyter_helpers.php
Normal file
203
includes/jupyter_helpers.php
Normal file
@@ -0,0 +1,203 @@
|
||||
<?php
|
||||
// includes/jupyter_helpers.php
|
||||
// Centralised helpers for Jupyter configuration and display logic.
|
||||
|
||||
/**
|
||||
* Returns the default configuration values documented on the Install & Configuration page.
|
||||
*
|
||||
* @return array<string, string>
|
||||
*/
|
||||
function dsp_jupyter_defaults(): array {
|
||||
return [
|
||||
'base_url' => 'https://localhost',
|
||||
'token' => 'dsp-token',
|
||||
'workspace_root' => 'datasources',
|
||||
'port' => '443',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines the external base URL for the Jupyter service.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
function dsp_jupyter_base_url(): string {
|
||||
$configured = getenv('JUPYTER_EXTERNAL_URL');
|
||||
if ($configured) {
|
||||
return rtrim($configured, '/');
|
||||
}
|
||||
|
||||
$defaults = dsp_jupyter_defaults();
|
||||
$scheme = (!empty($_SERVER['HTTPS']) && strtolower((string) $_SERVER['HTTPS']) !== 'off') ? 'https' : 'http';
|
||||
$hostHeader = $_SERVER['HTTP_HOST'] ?? '';
|
||||
|
||||
if ($hostHeader) {
|
||||
$hostname = explode(':', $hostHeader)[0];
|
||||
$port = dsp_jupyter_port();
|
||||
$authority = $hostname;
|
||||
|
||||
$portIsDefault = ($scheme === 'http' && $port === '80') || ($scheme === 'https' && $port === '443');
|
||||
if (!$portIsDefault) {
|
||||
$authority .= ':' . $port;
|
||||
}
|
||||
|
||||
return sprintf('%s://%s', $scheme, $authority);
|
||||
}
|
||||
|
||||
return rtrim($defaults['base_url'], '/');
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the token used to authenticate with Jupyter.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
function dsp_jupyter_token(): string {
|
||||
$authStrategy = strtolower((string) getenv('JUPYTERHUB_AUTH_STRATEGY'));
|
||||
if ($authStrategy === 'oauth') {
|
||||
return '';
|
||||
}
|
||||
|
||||
$defaults = dsp_jupyter_defaults();
|
||||
|
||||
$envToken = getenv('JUPYTER_TOKEN');
|
||||
if ($envToken !== false) {
|
||||
return $envToken;
|
||||
}
|
||||
|
||||
return $defaults['token'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves the username JupyterHub should use for the active user.
|
||||
*
|
||||
* The template can be customised via JUPYTERHUB_USERNAME_TEMPLATE.
|
||||
* Supported placeholders:
|
||||
* {person_id} - numeric person identifier from the session
|
||||
* {username} - DSP username from the session
|
||||
* {email} - Session email if available
|
||||
*
|
||||
* @param int|null $personId Optional explicit person ID
|
||||
* @return string|null Sanitised username or null when it cannot be determined.
|
||||
*/
|
||||
function dsp_resolve_jupyterhub_username(?int $personId, ?string $username = null, ?string $email = null): ?string {
|
||||
if ($personId === null || $personId <= 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$template = getenv('JUPYTERHUB_USERNAME_TEMPLATE') ?: 'user_{person_id}';
|
||||
$usernameRaw = str_replace(
|
||||
['{person_id}', '{username}', '{email}'],
|
||||
[
|
||||
(string) $personId,
|
||||
(string) $username,
|
||||
(string) $email,
|
||||
],
|
||||
$template
|
||||
);
|
||||
|
||||
$usernameSanitised = preg_replace('/[^a-zA-Z0-9._-]+/', '-', $usernameRaw);
|
||||
$usernameSanitised = trim((string) $usernameSanitised, "-_.");
|
||||
|
||||
return $usernameSanitised !== '' ? $usernameSanitised : null;
|
||||
}
|
||||
|
||||
function dsp_jupyterhub_username(?int $personId = null): ?string {
|
||||
if ($personId === null) {
|
||||
$personId = isset($_SESSION['person_id']) ? (int) $_SESSION['person_id'] : null;
|
||||
}
|
||||
|
||||
$username = $_SESSION['username'] ?? null;
|
||||
$email = $_SESSION['email'] ?? null;
|
||||
|
||||
return dsp_resolve_jupyterhub_username($personId, $username, $email);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the per-user route served by JupyterHub.
|
||||
*
|
||||
* The path template can be overridden with JUPYTERHUB_USER_PATH (e.g. "user/{username}/lab").
|
||||
*
|
||||
* @param string $baseUrl Hub base URL.
|
||||
* @param int|null $personId Optional explicit person ID.
|
||||
* @return string Absolute path including the user segment, without trailing slash.
|
||||
*/
|
||||
function dsp_jupyterhub_user_route(string $baseUrl, ?int $personId = null): string {
|
||||
$baseUrl = rtrim($baseUrl, '/');
|
||||
|
||||
$pathTemplate = getenv('JUPYTERHUB_USER_PATH');
|
||||
if ($pathTemplate === false || $pathTemplate === '') {
|
||||
return $baseUrl;
|
||||
}
|
||||
|
||||
$personId = $personId ?? (isset($_SESSION['person_id']) ? (int) $_SESSION['person_id'] : null);
|
||||
$username = dsp_jupyterhub_username($personId);
|
||||
|
||||
if ($username === null) {
|
||||
return $baseUrl;
|
||||
}
|
||||
|
||||
$relativePath = str_replace(
|
||||
['{username}', '{person_id}'],
|
||||
[$username, (string) $personId],
|
||||
ltrim($pathTemplate, '/')
|
||||
);
|
||||
|
||||
return rtrim($baseUrl . '/' . $relativePath, '/');
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the iframe URL using the resolved base URL and token.
|
||||
*
|
||||
* @param string|null $baseUrl Optional override of the base URL.
|
||||
* @param string|null $token Optional override of the token.
|
||||
* @param int|null $personId Optional override of the person ID used for the route.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
function dsp_jupyter_iframe_url(?string $baseUrl = null, ?string $token = null, ?int $personId = null): string {
|
||||
$resolvedBase = rtrim($baseUrl ?: dsp_jupyter_base_url(), '/');
|
||||
$userRoute = dsp_jupyterhub_user_route($resolvedBase, $personId);
|
||||
$finalToken = $token ?: dsp_jupyter_token();
|
||||
|
||||
return $finalToken
|
||||
? sprintf('%s?token=%s', $userRoute, urlencode($finalToken))
|
||||
: $userRoute;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines the host port the Jupyter service is published on.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
function dsp_jupyter_port(): string {
|
||||
$configured = getenv('JUPYTER_PORT');
|
||||
if ($configured === false || $configured === '') {
|
||||
$configured = getenv('JUPYTERHUB_PORT');
|
||||
}
|
||||
if ($configured) {
|
||||
return (string) $configured;
|
||||
}
|
||||
|
||||
$defaults = dsp_jupyter_defaults();
|
||||
|
||||
return $defaults['port'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Captures the environment overrides impacting the embedded Jupyter configuration.
|
||||
*
|
||||
* @return array<string, string|null>
|
||||
*/
|
||||
function dsp_jupyter_env_overrides(): array {
|
||||
return [
|
||||
'JUPYTER_EXTERNAL_URL' => getenv('JUPYTER_EXTERNAL_URL') ?: null,
|
||||
'JUPYTER_TOKEN' => getenv('JUPYTER_TOKEN') ?: null,
|
||||
'JUPYTER_PORT' => getenv('JUPYTER_PORT') ?: null,
|
||||
'JUPYTERHUB_PORT' => getenv('JUPYTERHUB_PORT') ?: null,
|
||||
'DSP_APP_ORIGINS' => getenv('DSP_APP_ORIGINS') ?: null,
|
||||
'DSP_FRAME_ANCESTORS' => getenv('DSP_FRAME_ANCESTORS') ?: null,
|
||||
'JUPYTERHUB_USERNAME_TEMPLATE' => getenv('JUPYTERHUB_USERNAME_TEMPLATE') ?: null,
|
||||
'JUPYTERHUB_USER_PATH' => getenv('JUPYTERHUB_USER_PATH') ?: null,
|
||||
];
|
||||
}
|
||||
Reference in New Issue
Block a user