Files
ponlork_1st/includes/jupyter_helpers.php
2026-01-29 14:31:48 +07:00

204 lines
6.1 KiB
PHP

<?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,
];
}