add(function() {
// Apply security headers
require_once __DIR__ . '/../app/includes/security_headers_middleware.php';
return true;
});
// For public pages, we don't need to validate the session
// The Router will handle authentication for protected pages
$validSession = false;
$userId = null;
// Only check session for non-public pages
if (!in_array($page, $public_pages)) {
$validSession = Session::isValidSession(true);
if ($validSession) {
$userId = Session::getUserId();
}
}
// Initialize feedback message system
require_once '../app/classes/feedback.php';
$system_messages = [];
require '../app/includes/errors.php';
// list of available pages
// edit accordingly, add 'pages/PAGE.php'
$allowed_urls = [
'dashboard',
'conferences','participants','components',
'graphs','latest','livejs','agents',
'profile','credentials','config','security',
'settings','theme','theme-asset',
'admin-tools',
'status',
'help','about',
'login','logout',
];
// Let plugins filter/extend allowed_urls
$allowed_urls = filter_allowed_urls($allowed_urls);
// Dispatch routing and auth
require_once __DIR__ . '/../app/core/Router.php';
use App\Core\Router;
$currentUser = Router::checkAuth($config, $app_root, $public_pages, $page);
// Connect to DB via DatabaseConnector
require_once __DIR__ . '/../app/core/DatabaseConnector.php';
use App\Core\DatabaseConnector;
$db = DatabaseConnector::connect($config);
// Logging: default to NullLogger, plugin can override
require_once __DIR__ . '/../app/core/NullLogger.php';
use App\Core\NullLogger;
$logObject = new NullLogger();
// Get the user IP
require_once __DIR__ . '/../app/helpers/ip_helper.php';
$user_IP = '';
// Plugin: initialize logging system plugin if available
do_hook('logger.system_init', ['db' => $db]);
// Override defaults if plugin provided real logger
if (isset($GLOBALS['logObject'])) {
$logObject = $GLOBALS['logObject'];
}
if (isset($GLOBALS['user_IP'])) {
$user_IP = $GLOBALS['user_IP'];
}
// Check for pending DB migrations (non-intrusive: warn only)
try {
$migrationsDir = __DIR__ . '/../doc/database/migrations';
if (is_dir($migrationsDir)) {
require_once __DIR__ . '/../app/core/MigrationRunner.php';
$runner = new \App\Core\MigrationRunner($db, $migrationsDir);
if ($runner->hasPendingMigrations()) {
$pending = $runner->listPendingMigrations();
$msg = 'Database schema is out of date. There are pending migrations. Run "php scripts/migrate.php up
" or use the Admin tools';
// Log and show as a system message
$logObject->log('warning', $msg, ['scope' => 'system']);
Feedback::flash('SYSTEM', 'MIGRATIONS_PENDING', $msg, false, true, false);
}
}
} catch (\Throwable $e) {
// Do not break the app; log only
error_log('Migration check failed: ' . $e->getMessage());
}
// CSRF middleware and run pipeline
$pipeline->add(function() {
// Initialize security middleware
require_once __DIR__ . '/../app/includes/csrf_middleware.php';
require_once __DIR__ . '/../app/helpers/security.php';
$security = SecurityHelper::getInstance();
// Verify CSRF token for POST requests
return applyCsrfMiddleware();
});
$pipeline->add(function() {
// Init rate limiter
global $db, $page, $userId;
require_once __DIR__ . '/../app/includes/rate_limit_middleware.php';
return checkRateLimit($db, $page, $userId);
});
$pipeline->add(function() {
// Init user functions
global $db, $userObject;
require_once __DIR__ . '/../app/classes/user.php';
include __DIR__ . '/../app/helpers/profile.php';
$userObject = new User($db);
return true;
});
if (!$pipeline->run()) {
exit;
}
// Maintenance mode: show maintenance page to non-superusers
try {
require_once __DIR__ . '/../app/core/Maintenance.php';
if (\App\Core\Maintenance::isEnabled()) {
$isSuperuser = false;
if ($validSession && isset($userId) && isset($userObject) && method_exists($userObject, 'hasRight')) {
// user 1 is always superuser per implementation, but also check explicit right
$isSuperuser = ($userId === 1) || (bool)$userObject->hasRight($userId, 'superuser');
}
if (!$isSuperuser) {
http_response_code(503);
// Advise clients to retry after 10 minutes (600 seconds; configure here)
header('Retry-After: 600');
// Show themed maintenance page
\App\Helpers\Theme::include('page-header');
\App\Helpers\Theme::include('page-menu');
include __DIR__ . '/../app/templates/maintenance.php';
\App\Helpers\Theme::include('page-footer');
ob_end_flush();
exit;
} else {
// Superusers bypass maintenance; show a small banner
$maintMsg = \App\Core\Maintenance::getMessage();
$custom = 'Maintenance mode is enabled.';
if (!empty($maintMsg)) {
$custom .= ' ' . htmlspecialchars($maintMsg) . '';
}
$custom .= ' Control it in Admin tools';
// Non-dismissible and small, do not sanitize to allow link and
Feedback::flash('SYSTEM', 'MAINTENANCE_ON', $custom, false, true, false);
}
}
} catch (\Throwable $e) {
// Do not break app if maintenance check fails
}
// Apply per-user theme from DB into session (without persisting) once user is known
if ($validSession && isset($userId) && isset($userObject) && is_object($userObject) && method_exists($userObject, 'getUserTheme')) {
try {
$dbTheme = $userObject->getUserTheme((int)$userId);
if ($dbTheme) {
\App\Helpers\Theme::setCurrentTheme($dbTheme, false);
}
} catch (\Throwable $e) {
// Non-fatal if theme load fails
}
}
// get platforms details
require '../app/classes/platform.php';
$platformObject = new Platform($db);
$platformsAll = $platformObject->getPlatformDetails();
// by default we connect ot the first configured platform
if ($platform_id == '') {
$platform_id = $platformsAll[0]['id'];
}
$platformDetails = $platformObject->getPlatformDetails($platform_id);
// logout is a special case, as we can't use session vars for notices
if ($page == 'logout') {
// Save config before destroying session
$savedConfig = $config;
// clean up session
Session::destroySession();
// start new session for the login page
Session::startSession();
// Restore config to global scope
$config = $savedConfig;
$GLOBALS['config'] = $config;
setcookie('username', "", time() - 100, $config['folder'], $config['domain'], isset($_SERVER['HTTPS']), true);
// Log successful logout
$logObject->log('info', "Logout: User \"$currentUser\" logged out. IP: $user_IP", ['user_id' => $userId, 'scope' => 'user']);
// Set success message
Feedback::flash('LOGIN', 'LOGOUT_SUCCESS');
// Use theme helper to include templates
\App\Helpers\Theme::include('page-header');
\App\Helpers\Theme::include('page-menu');
include '../app/pages/login.php';
\App\Helpers\Theme::include('page-footer');
} else {
// if user is logged in, we need user details and rights
if ($validSession) {
// If by error a logged in user requests the login page
if ($page === 'login') {
header('Location: ' . htmlspecialchars($app_root));
exit();
}
$userDetails = $userObject->getUserDetails($userId);
$userRights = $userObject->getUserRights($userId);
$userTimezone = (!empty($userDetails[0]['timezone'])) ? $userDetails[0]['timezone'] : 'UTC'; // Default to UTC if no timezone is set (or is missing)
// check if the Jilo Server is running
require '../app/classes/server.php';
$serverObject = new Server($db);
$server_host = '127.0.0.1';
$server_port = '8080';
$server_endpoint = '/health';
$server_status = $serverObject->getServerStatus($server_host, $server_port, $server_endpoint);
if (!$server_status) {
Feedback::flash('ERROR', 'DEFAULT', 'The Jilo Server is not running. Some data may be old and incorrect.', false, true);
}
}
// --- Plugin loading logic for all enabled plugins ---
// Ensure all enabled plugin bootstraps are loaded before mapping controllers
foreach ($GLOBALS['enabled_plugins'] as $plugin_name => $plugin_info) {
$bootstrap_path = $plugin_info['path'] . '/bootstrap.php';
if (file_exists($bootstrap_path)) {
require_once $bootstrap_path;
}
}
// Plugin controller mapping logic (we add each controller listed in bootstrap as a page)
$mapped_plugin_controllers = [];
foreach ($GLOBALS['enabled_plugins'] as $plugin_name => $plugin_info) {
if (isset($GLOBALS['plugin_controllers'][$plugin_name])) {
foreach ($GLOBALS['plugin_controllers'][$plugin_name] as $plugin_page) {
$controller_path = $plugin_info['path'] . '/controllers/' . $plugin_page . '.php';
if (file_exists($controller_path)) {
$mapped_plugin_controllers[$plugin_page] = $controller_path;
}
}
}
}
// page building
if (in_array($page, $allowed_urls)) {
// The page is in allowed URLs
if (isset($mapped_plugin_controllers[$page]) && file_exists($mapped_plugin_controllers[$page])) {
// The page is from a plugin controller
if (defined('PLUGIN_PAGE_DIRECT_OUTPUT') && PLUGIN_PAGE_DIRECT_OUTPUT === true) {
// Barebone page controller, we don't output anything extra
include $mapped_plugin_controllers[$page];
ob_end_flush();
exit;
} else {
\App\Helpers\Theme::include('page-header');
\App\Helpers\Theme::include('page-menu');
if ($validSession) {
\App\Helpers\Theme::include('page-sidebar');
}
include $mapped_plugin_controllers[$page];
\App\Helpers\Theme::include('page-footer');
}
} else {
// The page is from a core controller
\App\Helpers\Theme::include('page-header');
\App\Helpers\Theme::include('page-menu');
if ($validSession) {
\App\Helpers\Theme::include('page-sidebar');
}
if (file_exists("../app/pages/{$page}.php")) {
include "../app/pages/{$page}.php";
} else {
include '../app/templates/error-notfound.php';
}
\App\Helpers\Theme::include('page-footer');
}
} else {
// The page is not in allowed URLs
\App\Helpers\Theme::include('page-header');
\App\Helpers\Theme::include('page-menu');
if ($validSession) {
\App\Helpers\Theme::include('page-sidebar');
}
include '../app/templates/error-notfound.php';
\App\Helpers\Theme::include('page-footer');
}
}
// flush the output buffer and show the page
ob_end_flush();
// clear errors and notices before next page just in case
unset($_SESSION['error']);
unset($_SESSION['notice']);