Panel/Panel/modules/website/includes/bootstrap.php

1019 lines
29 KiB
PHP

<?php
declare(strict_types=1);
require_once __DIR__ . '/paths.php';
if (defined('GSP_WEBSITE_BOOTSTRAPPED')) {
return;
}
define('GSP_WEBSITE_BOOTSTRAPPED', true);
error_reporting(E_ALL);
ini_set('display_errors', '0');
$websiteConfig = [];
$websiteConfigFiles = [
WEBSITE_CONFIG_DIR . '/pricing.php',
WEBSITE_CONFIG_DIR . '/services.php',
WEBSITE_CONFIG_DIR . '/config.php',
WEBSITE_CONFIG_DIR . '/config.local.php',
];
foreach ($websiteConfigFiles as $configFile) {
if (!is_readable($configFile)) {
continue;
}
$loaded = require $configFile;
if (is_array($loaded)) {
$websiteConfig = array_replace_recursive($websiteConfig, $loaded);
}
}
if (is_readable(__DIR__ . '/billing.php')) {
require_once __DIR__ . '/billing.php';
}
$websiteDefaults = [
'site_name' => 'Gameservers.World',
'site_tagline' => 'Developer-backed game hosting for modern and legacy communities, with full server access, daily backups, and optional custom engineering help through Runlevel Systems.',
'meta_description' => 'Affordable game servers for modern and legacy communities, backed by developers, software engineers, and infrastructure specialists. Launch a standard server or get help with mods, automation, integrations, and custom tooling.',
'base_path' => null,
'public_base_url' => null,
'billing_base_url' => '/billing',
'panel_url' => 'https://panel.iaregamer.com/',
'login_url' => 'https://panel.iaregamer.com/',
'company' => [
'name' => 'Runlevel Systems',
'url' => 'https://runlevelsystems.com/',
'copyright' => "\u{00A9} 2026 Runlevel Systems",
],
'services' => [
'project_request_url' => 'https://runlevelsystems.com/start-project.php',
],
'discord_url' => null,
'support_url' => null,
'support_email' => null,
'admin_notice' => 'Server catalog is currently unavailable. Please contact support.',
'locations' => [
['name' => 'Los Angeles, USA', 'region' => 'West Coast coverage', 'host' => 'la-game-1.iaregamer.com'],
['name' => 'Kansas City, USA', 'region' => 'Central US coverage', 'host' => 'kc-game-2.iaregamer.com'],
['name' => 'Dallas, USA', 'region' => 'Southern US coverage', 'host' => 'dal-game-1.iaregamer.com'],
['name' => 'New York City, USA', 'region' => 'East Coast coverage', 'host' => 'nyc-game-1.iaregamer.com'],
['name' => 'Dublin, Ireland', 'region' => 'EU coverage', 'host' => 'dub-game-1.iaregamer.com'],
],
];
$websiteConfig = array_replace_recursive($websiteDefaults, $websiteConfig);
function website_start_session(): void
{
if (session_status() === PHP_SESSION_ACTIVE) {
return;
}
$secure = website_request_scheme() === 'https';
session_set_cookie_params([
'lifetime' => 0,
'path' => website_base_path() === '' ? '/' : website_base_path(),
'secure' => $secure,
'httponly' => true,
'samesite' => 'Lax',
]);
session_start();
$now = time();
$inactiveLimit = 3600;
$absoluteLimit = 43200;
if (!isset($_SESSION['website_session_started_at'])) {
$_SESSION['website_session_started_at'] = $now;
}
if (isset($_SESSION['website_last_seen_at']) && ($now - (int)$_SESSION['website_last_seen_at']) > $inactiveLimit) {
$_SESSION = [];
session_destroy();
session_start();
$_SESSION['website_session_started_at'] = $now;
}
if (($now - (int)($_SESSION['website_session_started_at'] ?? $now)) > $absoluteLimit) {
$_SESSION = [];
session_destroy();
session_start();
$_SESSION['website_session_started_at'] = $now;
}
$_SESSION['website_last_seen_at'] = $now;
}
function website_config(?string $key = null, $default = null)
{
global $websiteConfig;
if ($key === null) {
return $websiteConfig;
}
return $websiteConfig[$key] ?? $default;
}
function website_log(string $message): void
{
error_log('[website] ' . $message);
}
function website_error_reference(): string
{
try {
return strtoupper(bin2hex(random_bytes(4)));
} catch (Throwable $e) {
return strtoupper(substr(md5((string)microtime(true)), 0, 8));
}
}
function website_render_fatal_error(string $reference): void
{
if (!headers_sent()) {
http_response_code(500);
header('Content-Type: text/html; charset=utf-8');
}
echo '<!doctype html><html lang="en"><head><meta charset="utf-8">';
echo '<meta name="viewport" content="width=device-width, initial-scale=1">';
echo '<title>Website Error - Gameservers.World</title>';
echo '<link rel="stylesheet" href="' . website_escape(website_asset('css/site.css')) . '">';
echo '</head><body><main class="site-main"><section class="page-heading"><div class="container">';
echo '<h1>Something went wrong</h1>';
echo '<p>We could not load this page. Please try again or contact support with reference ';
echo website_escape($reference) . '.</p>';
echo '</div></section></main></body></html>';
}
register_shutdown_function(static function (): void {
$error = error_get_last();
if (!is_array($error)) {
return;
}
$fatalTypes = [E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR, E_USER_ERROR, E_RECOVERABLE_ERROR];
if (!in_array((int)$error['type'], $fatalTypes, true)) {
return;
}
$reference = website_error_reference();
website_log('Fatal error [' . $reference . '] ' . ($error['message'] ?? 'unknown') . ' in ' . ($error['file'] ?? 'unknown') . ':' . (string)($error['line'] ?? '0'));
if (!headers_sent()) {
while (ob_get_level() > 0) {
ob_end_clean();
}
website_render_fatal_error($reference);
}
});
function website_escape($value): string
{
return htmlspecialchars((string)$value, ENT_QUOTES, 'UTF-8');
}
function website_normalize_base_path(?string $path): string
{
$path = trim((string)$path);
if ($path === '' || $path === '/') {
return '';
}
return '/' . trim($path, '/');
}
function website_request_scheme(): string
{
$https = $_SERVER['HTTPS'] ?? '';
if ($https !== '' && strtolower((string)$https) !== 'off') {
return 'https';
}
$forwarded = $_SERVER['HTTP_X_FORWARDED_PROTO'] ?? '';
if ($forwarded !== '') {
return strtolower((string)$forwarded) === 'https' ? 'https' : 'http';
}
return 'http';
}
function website_request_method(): string
{
return strtoupper((string)($_SERVER['REQUEST_METHOD'] ?? 'GET'));
}
function website_base_path(): string
{
static $basePath = null;
if ($basePath !== null) {
return $basePath;
}
$configured = website_config('base_path');
if (is_string($configured) && $configured !== '') {
$basePath = website_normalize_base_path($configured);
return $basePath;
}
$scriptName = (string)($_SERVER['SCRIPT_NAME'] ?? '');
if ($scriptName === '') {
$basePath = '';
return $basePath;
}
$dir = str_replace('\\', '/', dirname($scriptName));
$basePath = ($dir === '/' || $dir === '.' || $dir === '') ? '' : website_normalize_base_path($dir);
return $basePath;
}
function website_public_base_url(): string
{
static $baseUrl = null;
if ($baseUrl !== null) {
return $baseUrl;
}
$configured = trim((string)website_config('public_base_url', ''));
if ($configured !== '') {
$baseUrl = rtrim($configured, '/');
return $baseUrl;
}
$host = trim((string)($_SERVER['HTTP_HOST'] ?? ''));
if ($host === '') {
$baseUrl = '';
return $baseUrl;
}
$baseUrl = website_request_scheme() . '://' . $host . website_base_path();
return $baseUrl;
}
function website_url(string $path = ''): string
{
$basePath = rtrim(website_base_path(), '/');
$path = ltrim($path, '/');
if ($path === '') {
return $basePath === '' ? '/' : $basePath . '/';
}
return ($basePath === '' ? '' : $basePath) . '/' . $path;
}
function website_asset(string $path): string
{
return website_url('assets/' . ltrim($path, '/'));
}
function website_join_external_url(string $base, string $path = ''): string
{
$base = trim($base);
if ($base === '') {
return website_url($path);
}
$base = rtrim($base, '/');
$path = ltrim($path, '/');
if ($path === '') {
return $base . '/';
}
return $base . '/' . $path;
}
function panel_url(string $path = ''): string
{
return website_join_external_url((string)website_config('panel_url', ''), $path);
}
function login_url(string $path = ''): string
{
return website_join_external_url((string)website_config('login_url', website_config('panel_url', '')), $path);
}
function billing_url(string $path = ''): string
{
return website_join_external_url((string)website_config('billing_base_url', ''), $path);
}
function documentation_url(?string $docSlug = null): string
{
if ($docSlug === null || $docSlug === '') {
return website_url('docs.php');
}
return website_url('docs.php?doc=' . rawurlencode($docSlug));
}
function website_canonical_url(string $path = ''): string
{
$base = website_public_base_url();
if ($base === '') {
return website_url($path);
}
$path = ltrim($path, '/');
if ($path === '') {
return $base . '/';
}
return rtrim($base, '/') . '/' . $path;
}
function website_read_php_assignments(string $filePath, array $variableNames): array
{
if (!is_readable($filePath)) {
return [];
}
$content = @file_get_contents($filePath);
if ($content === false) {
return [];
}
$result = [];
foreach ($variableNames as $variableName) {
$patternDouble = '/^\s*\$' . preg_quote($variableName, '/') . '\s*=\s*"([^"]*)"/m';
$patternSingle = '/^\s*\$' . preg_quote($variableName, '/') . "\s*=\s*'([^']*)'/m";
if (preg_match($patternDouble, $content, $match) === 1 || preg_match($patternSingle, $content, $match) === 1) {
$result[$variableName] = $match[1];
}
}
return $result;
}
function website_database_settings(): ?array
{
static $settings = null;
static $resolved = false;
if ($resolved) {
return $settings;
}
$resolved = true;
$keys = ['db_host', 'db_port', 'db_user', 'db_pass', 'db_name', 'table_prefix', 'db_type'];
$merged = [];
$panelConfig = WEBSITE_PANEL_INCLUDE_DIR . '/config.inc.php';
if (is_readable($panelConfig)) {
$merged = array_replace($merged, website_read_php_assignments($panelConfig, $keys));
}
$billingConfig = WEBSITE_BILLING_ROOT . '/includes/config.inc.php';
if (is_readable($billingConfig)) {
$merged = array_replace($merged, website_read_php_assignments($billingConfig, $keys));
}
foreach (['db_host', 'db_user', 'db_name'] as $requiredKey) {
if (empty($merged[$requiredKey])) {
$settings = null;
return $settings;
}
}
$settings = $merged;
return $settings;
}
function website_billing_config_present(): bool
{
return is_readable(WEBSITE_BILLING_ROOT . '/includes/config.inc.php');
}
function website_db(): ?mysqli
{
static $connection = false;
if ($connection instanceof mysqli) {
return $connection;
}
if ($connection === null) {
return null;
}
$settings = website_database_settings();
if ($settings === null) {
$connection = null;
return null;
}
$port = isset($settings['db_port']) && $settings['db_port'] !== '' ? (int)$settings['db_port'] : null;
$mysqli = @mysqli_connect(
(string)$settings['db_host'],
(string)($settings['db_user'] ?? ''),
(string)($settings['db_pass'] ?? ''),
(string)$settings['db_name'],
$port
);
if (!$mysqli instanceof mysqli) {
website_log('Database connection failed for public website.');
$connection = null;
return null;
}
@mysqli_set_charset($mysqli, 'utf8mb4');
$connection = $mysqli;
return $connection;
}
function website_table_prefix(): string
{
$settings = website_database_settings();
return (string)($settings['table_prefix'] ?? '');
}
function website_billing_available(): bool
{
return website_db() instanceof mysqli;
}
function website_table_exists(string $tableName): bool
{
$db = website_db();
if (!$db instanceof mysqli || $tableName === '') {
return false;
}
$stmt = $db->prepare('SHOW TABLES LIKE ?');
if (!$stmt) {
return false;
}
$stmt->bind_param('s', $tableName);
$stmt->execute();
$result = $stmt->get_result();
$exists = $result instanceof mysqli_result && $result->num_rows > 0;
$stmt->close();
return $exists;
}
function website_table_columns(string $tableName): array
{
$db = website_db();
if (!$db instanceof mysqli || $tableName === '') {
return [];
}
$safeTable = str_replace('`', '``', $tableName);
$result = @$db->query("SHOW COLUMNS FROM `{$safeTable}`");
if (!$result instanceof mysqli_result) {
return [];
}
$columns = [];
while ($row = $result->fetch_assoc()) {
$columns[(string)$row['Field']] = true;
}
$result->free();
return $columns;
}
function website_panel_user_by_id(int $userId): ?array
{
$db = website_db();
$prefix = website_table_prefix();
if (!$db instanceof mysqli || $userId <= 0) {
return null;
}
$table = $db->real_escape_string($prefix . 'users');
$stmt = $db->prepare("SELECT * FROM `{$table}` WHERE `user_id` = ? LIMIT 1");
if (!$stmt) {
return null;
}
$stmt->bind_param('i', $userId);
$stmt->execute();
$result = $stmt->get_result();
$user = $result instanceof mysqli_result ? $result->fetch_assoc() : null;
$stmt->close();
return is_array($user) ? $user : null;
}
function website_panel_user_by_login(string $login): ?array
{
$db = website_db();
$prefix = website_table_prefix();
if (!$db instanceof mysqli || $login === '') {
return null;
}
$table = $db->real_escape_string($prefix . 'users');
$stmt = $db->prepare("SELECT * FROM `{$table}` WHERE `users_login` = ? LIMIT 1");
if (!$stmt) {
return null;
}
$stmt->bind_param('s', $login);
$stmt->execute();
$result = $stmt->get_result();
$user = $result instanceof mysqli_result ? $result->fetch_assoc() : null;
$stmt->close();
return is_array($user) ? $user : null;
}
function website_verify_panel_password(array $user, string $password): bool
{
$hash = (string)($user['users_passwd'] ?? '');
if ($hash === '') {
return false;
}
return hash_equals($hash, md5($password));
}
function website_authenticate_user(string $login, string $password): ?array
{
$user = website_panel_user_by_login($login);
if (!$user || !website_verify_panel_password($user, $password)) {
return null;
}
if ((string)($user['users_role'] ?? '') === 'banned') {
return null;
}
return $user;
}
function website_set_user_session(array $user): void
{
website_start_session();
session_regenerate_id(true);
$_SESSION['website_user_id'] = (int)$user['user_id'];
$_SESSION['website_users_login'] = (string)$user['users_login'];
$_SESSION['website_users_role'] = (string)$user['users_role'];
$_SESSION['website_login_at'] = time();
}
function website_logout_user(): void
{
website_start_session();
$_SESSION = [];
if (ini_get('session.use_cookies')) {
$params = session_get_cookie_params();
setcookie(session_name(), '', time() - 42000, $params['path'], $params['domain'] ?? '', (bool)$params['secure'], (bool)$params['httponly']);
}
session_destroy();
}
function website_current_user(): ?array
{
website_start_session();
$userId = (int)($_SESSION['website_user_id'] ?? 0);
if ($userId <= 0) {
return null;
}
$user = website_panel_user_by_id($userId);
if (!$user) {
website_logout_user();
return null;
}
return $user;
}
function website_is_logged_in(): bool
{
return website_current_user() !== null;
}
function website_current_user_is_staff(): bool
{
$user = website_current_user();
return $user !== null && (string)($user['users_role'] ?? '') === 'admin';
}
function website_log_activity(string $message, int $userId = 0, string $eventType = 'website'): void
{
$db = website_db();
$prefix = website_table_prefix();
if (!$db instanceof mysqli) {
return;
}
$table = $prefix . 'logger';
if (!website_table_exists($table)) {
return;
}
$safeTable = $db->real_escape_string($table);
$ip = substr((string)($_SERVER['REMOTE_ADDR'] ?? ''), 0, 255);
$stmt = $db->prepare(
"INSERT INTO `{$safeTable}` (`date`, `user_id`, `ip`, `message`, `source_type`, `category`, `event_type`, `severity`)
VALUES (FROM_UNIXTIME(UNIX_TIMESTAMP(), '%d-%m-%Y %H:%i:%s'), ?, ?, ?, 'website', 'authentication', ?, 'info')"
);
if (!$stmt) {
return;
}
$message = substr($message, 0, 1000);
$eventType = substr($eventType, 0, 80);
$stmt->bind_param('isss', $userId, $ip, $message, $eventType);
@$stmt->execute();
$stmt->close();
}
function website_safe_return_path(string $returnPath, string $default = 'index.php'): string
{
if ($returnPath === '' || preg_match('#^[a-z][a-z0-9+.-]*://#i', $returnPath) === 1 || strpos($returnPath, '//') === 0) {
return $default;
}
$returnPath = ltrim($returnPath, '/');
if (strpos($returnPath, "\0") !== false || strpos($returnPath, '../') === 0 || strpos($returnPath, '/../') !== false) {
return $default;
}
return $returnPath;
}
function website_login_url(string $returnPath = ''): string
{
$path = 'login.php';
if ($returnPath !== '') {
$path .= '?return=' . rawurlencode(website_safe_return_path($returnPath, 'index.php'));
}
return website_url($path);
}
function website_control_panel_url(string $returnPath = 'home.php?m=dashboard&p=dashboard'): string
{
return panel_url(website_safe_return_path($returnPath, 'home.php?m=dashboard&p=dashboard'));
}
function website_order_url($serviceId): string
{
return website_url('order.php?service_id=' . rawurlencode((string)$serviceId));
}
function website_cart_url(): string
{
return website_url('cart.php');
}
function website_checkout_url(): string
{
return website_url('cart.php?checkout=1');
}
function website_register_url(string $returnPath = 'cart.php'): string
{
$path = 'register.php';
if ($returnPath !== '') {
$path .= '?return=' . rawurlencode(website_safe_return_path($returnPath, 'cart.php'));
}
return website_url($path);
}
function website_fetch_service_by_id(int $serviceId): ?array
{
$db = website_db();
$prefix = website_table_prefix();
if (!$db instanceof mysqli || $serviceId <= 0) {
return null;
}
$serviceTable = $prefix . 'billing_services';
if (!website_table_exists($serviceTable)) {
return null;
}
$safeServiceTable = $db->real_escape_string($serviceTable);
$configTable = $prefix . 'config_homes';
if (website_table_exists($configTable) && website_column_exists($serviceTable, 'home_cfg_id')) {
$safeConfigTable = $db->real_escape_string($configTable);
$sql = "SELECT bs.*, ch.game_name AS cfg_game_name, ch.game_key AS cfg_game_key, ch.home_cfg_file AS cfg_file
FROM `{$safeServiceTable}` bs
LEFT JOIN `{$safeConfigTable}` ch ON ch.home_cfg_id = bs.home_cfg_id
WHERE bs.service_id = ?
LIMIT 1";
} else {
$sql = "SELECT bs.*, '' AS cfg_game_name, '' AS cfg_game_key, '' AS cfg_file
FROM `{$safeServiceTable}` bs
WHERE bs.service_id = ?
LIMIT 1";
}
$stmt = $db->prepare($sql);
if (!$stmt) {
return null;
}
$stmt->bind_param('i', $serviceId);
$stmt->execute();
$result = $stmt->get_result();
$service = $result instanceof mysqli_result ? $result->fetch_assoc() : null;
$stmt->close();
if (!is_array($service)) {
return null;
}
if (array_key_exists('enabled', $service) && (int)$service['enabled'] !== 1) {
return null;
}
return $service;
}
function website_service_name(array $service): string
{
$name = trim((string)($service['cfg_game_name'] ?? ''));
if ($name === '') {
$name = trim((string)($service['service_name'] ?? ''));
}
return $name === '' ? 'Game Server' : $name;
}
function website_service_min_slots(array $service): int
{
foreach (['slot_min_qty', 'min_slots', 'minimum_slots', 'slots_min'] as $column) {
if (isset($service[$column]) && (int)$service[$column] > 0) {
return (int)$service[$column];
}
}
$pricing = website_config('pricing', []);
return max(1, (int)($pricing['standard_min_slots'] ?? 16));
}
function website_service_max_slots(array $service): int
{
foreach (['slot_max_qty', 'max_slots', 'maximum_slots', 'slots_max', 'max_players'] as $column) {
if (isset($service[$column]) && (int)$service[$column] > 0) {
return (int)$service[$column];
}
}
return 0;
}
function website_service_locations(array $service): array
{
$raw = trim((string)($service['remote_server_id'] ?? ''));
if ($raw === '') {
return [];
}
$locations = [];
foreach (preg_split('/[\s,]+/', $raw) ?: [] as $remoteServerId) {
$remoteServerId = trim($remoteServerId);
if ($remoteServerId === '' || !ctype_digit($remoteServerId)) {
continue;
}
$locations[$remoteServerId] = 'Location ' . $remoteServerId;
}
return $locations;
}
function website_cart_items(): array
{
website_start_session();
return is_array($_SESSION['website_cart'] ?? null) ? $_SESSION['website_cart'] : [];
}
function website_cart_count(): int
{
return count(website_cart_items());
}
function website_cart_add(array $item): void
{
website_start_session();
if (!isset($_SESSION['website_cart']) || !is_array($_SESSION['website_cart'])) {
$_SESSION['website_cart'] = [];
}
$key = bin2hex(random_bytes(8));
$_SESSION['website_cart'][$key] = $item;
}
function website_cart_remove(string $key): void
{
website_start_session();
if (isset($_SESSION['website_cart'][$key])) {
unset($_SESSION['website_cart'][$key]);
}
}
function website_cart_total(): float
{
$total = 0.0;
foreach (website_cart_items() as $item) {
$total += (float)($item['line_total'] ?? $item['monthly_total'] ?? 0);
}
return $total;
}
function website_billing_docs_root(): ?string
{
if (is_dir(WEBSITE_BILLING_DOCS_DIR)) {
return WEBSITE_BILLING_DOCS_DIR;
}
$legacyDocs = WEBSITE_LEGACY_SITE_ROOT . '/docs';
if (is_dir($legacyDocs)) {
return $legacyDocs;
}
return null;
}
function website_is_valid_doc_slug(string $slug): bool
{
return (bool)preg_match('/^[a-z0-9][a-z0-9_-]*$/i', $slug);
}
function website_doc_path(string $slug, string $fileName = 'index.php'): ?string
{
if (!website_is_valid_doc_slug($slug)) {
return null;
}
$docsRoot = website_billing_docs_root();
if ($docsRoot === null) {
return null;
}
$candidate = realpath($docsRoot . '/' . $slug . '/' . $fileName);
if ($candidate === false || strpos($candidate, realpath($docsRoot) ?: $docsRoot) !== 0) {
return null;
}
return $candidate;
}
function website_doc_icon_url(string $slug): ?string
{
foreach (['icon.png', 'icon.jpg', 'icon.jpeg', 'icon.webp'] as $fileName) {
if (website_doc_path($slug, $fileName) !== null) {
return website_url('doc_asset.php?doc=' . rawurlencode($slug) . '&file=' . rawurlencode($fileName));
}
}
return null;
}
function website_service_image_url(string $imageValue): string
{
$imageValue = trim($imageValue);
if ($imageValue === '') {
return website_asset('images/banner.png');
}
if (preg_match('#^https?://#i', $imageValue) === 1) {
return $imageValue;
}
$fileName = basename($imageValue);
if ($fileName === '') {
return website_asset('images/banner.png');
}
return website_asset('images/games/' . $fileName);
}
function website_fetch_services(int $limit = 0, bool $includeDisabled = false): array
{
$db = website_db();
if (!$db instanceof mysqli) {
return [];
}
$prefix = website_table_prefix();
$serviceTable = $prefix . 'billing_services';
$configTable = $prefix . 'config_homes';
if (!website_table_exists($serviceTable)) {
return [];
}
$serviceColumns = website_table_columns($serviceTable);
$hasEnabled = isset($serviceColumns['enabled']);
$hasRemoteServerId = isset($serviceColumns['remote_server_id']);
$where = $includeDisabled ? '1 = 1' : '1 = 1';
if (!$includeDisabled && $hasEnabled) {
$where .= ' AND bs.enabled = 1';
}
if (!$includeDisabled && $hasRemoteServerId) {
$where .= " AND bs.remote_server_id <> '' AND bs.remote_server_id IS NOT NULL";
}
if (website_table_exists($configTable) && isset($serviceColumns['home_cfg_id'])) {
$sql = "SELECT bs.*,
ch.game_name AS cfg_game_name,
ch.game_key AS cfg_game_key,
ch.home_cfg_file AS cfg_file
FROM `{$serviceTable}` bs
LEFT JOIN `{$configTable}` ch ON ch.home_cfg_id = bs.home_cfg_id
WHERE {$where}
ORDER BY bs.service_name ASC";
} else {
$sql = "SELECT bs.*, '' AS cfg_game_name, '' AS cfg_game_key, '' AS cfg_file
FROM `{$serviceTable}` bs
WHERE {$where}
ORDER BY bs.service_name ASC";
}
if ($limit > 0) {
$sql .= ' LIMIT ' . max(1, $limit);
}
$result = @$db->query($sql);
if (!$result instanceof mysqli_result) {
website_log('Failed to query billing services for website catalog.');
return [];
}
$rows = [];
while ($row = $result->fetch_assoc()) {
$rows[] = $row;
}
$result->free();
return $rows;
}
function website_custom_project_url(): string
{
$services = website_config('services', []);
$projectUrl = '';
if (is_array($services)) {
$projectUrl = trim((string)($services['project_request_url'] ?? ''));
}
if ($projectUrl !== '') {
return $projectUrl;
}
$supportUrl = trim((string)website_config('support_url', ''));
if ($supportUrl !== '') {
return $supportUrl;
}
$discordUrl = trim((string)website_config('discord_url', ''));
if ($discordUrl !== '') {
return $discordUrl;
}
return website_url('support.php');
}
function website_fetch_doc_index(): array
{
$docsRoot = website_billing_docs_root();
if ($docsRoot === null || !is_dir($docsRoot)) {
return [];
}
$entries = [];
foreach (array_diff(scandir($docsRoot) ?: [], ['.', '..']) as $folder) {
$docFolder = $docsRoot . '/' . $folder;
if (!is_dir($docFolder)) {
continue;
}
$indexPath = website_doc_path($folder, 'index.php');
$metadataPath = website_doc_path($folder, 'metadata.json');
if ($indexPath === null || $metadataPath === null) {
continue;
}
$metadataContent = @file_get_contents($metadataPath);
$metadataContent = $metadataContent === false ? '' : preg_replace('/^\xEF\xBB\xBF/', '', $metadataContent);
$metadata = json_decode((string)$metadataContent, true);
if (!is_array($metadata)) {
$metadata = [];
}
$entries[] = [
'slug' => $folder,
'name' => (string)($metadata['name'] ?? ucwords(str_replace(['-', '_'], ' ', $folder))),
'description' => (string)($metadata['description'] ?? ''),
'category' => (string)($metadata['category'] ?? 'other'),
'order' => (int)($metadata['order'] ?? 999),
'complete' => (bool)($metadata['complete'] ?? true),
'icon_url' => website_doc_icon_url($folder),
];
}
usort(
$entries,
static function (array $left, array $right): int {
if ($left['category'] !== $right['category']) {
return strcmp($left['category'], $right['category']);
}
if ($left['order'] !== $right['order']) {
return $left['order'] <=> $right['order'];
}
return strcasecmp($left['name'], $right['name']);
}
);
return $entries;
}
function website_render(string $pageTemplate, array $context = []): void
{
extract($context, EXTR_SKIP);
require WEBSITE_INCLUDE_DIR . '/header.php';
require WEBSITE_ROOT_DIR . '/pages/' . $pageTemplate;
require WEBSITE_INCLUDE_DIR . '/footer.php';
}
website_start_session();