feat: Add comprehensive documentation suite and reorganize project structure
- Created complete documentation in docs/ directory - Added PROJECT_OVERVIEW.md with feature highlights and getting started guide - Added ARCHITECTURE.md with system design and technical details - Added SECURITY.md with comprehensive security implementation guide - Added DEVELOPMENT.md with development workflows and best practices - Added DEPLOYMENT.md with production deployment instructions - Added API.md with complete REST API documentation - Added CONTRIBUTING.md with contribution guidelines - Added CHANGELOG.md with version history and migration notes - Reorganized all documentation files into docs/ directory for better organization - Updated README.md with proper documentation links and quick navigation - Enhanced project structure with professional documentation standards
This commit is contained in:
182
admin/includes/bootstrap.php
Normal file
182
admin/includes/bootstrap.php
Normal file
@@ -0,0 +1,182 @@
|
||||
<?php
|
||||
/**
|
||||
* Admin panel bootstrap helpers.
|
||||
* Handles session protection and PDO access for lightweight admin tools.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
define('_ISVALID', true);
|
||||
|
||||
if (session_status() !== PHP_SESSION_ACTIVE) {
|
||||
session_start();
|
||||
}
|
||||
|
||||
if (empty($_SESSION['admin_logged_in'])) {
|
||||
header('Location: /login.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve an environment variable or fallback to .env contents/default value.
|
||||
*/
|
||||
function admin_env(string $key, ?string $default = null): ?string
|
||||
{
|
||||
$fromRuntime = getenv($key);
|
||||
if ($fromRuntime !== false) {
|
||||
return $fromRuntime;
|
||||
}
|
||||
|
||||
if (isset($_ENV[$key])) {
|
||||
return $_ENV[$key];
|
||||
}
|
||||
|
||||
static $envFile = null;
|
||||
if ($envFile === null) {
|
||||
$envFile = [];
|
||||
$path = dirname(__DIR__, 2) . DIRECTORY_SEPARATOR . '.env';
|
||||
if (is_readable($path)) {
|
||||
foreach (file($path, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES) as $line) {
|
||||
if (str_starts_with($line, '#') || !str_contains($line, '=')) {
|
||||
continue;
|
||||
}
|
||||
[$envKey, $envValue] = array_map('trim', explode('=', $line, 2));
|
||||
$envFile[$envKey] = $envValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $envFile[$key] ?? $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shared PDO handle for admin tooling.
|
||||
*/
|
||||
function admin_pdo(): PDO
|
||||
{
|
||||
static $pdo = null;
|
||||
|
||||
if ($pdo instanceof PDO) {
|
||||
return $pdo;
|
||||
}
|
||||
|
||||
$host = admin_env('DB_HOST', 'db');
|
||||
$dbname = admin_env('DB_NAME', 'easystream');
|
||||
$user = admin_env('DB_USER', 'easystream');
|
||||
$pass = admin_env('DB_PASS', 'easystream');
|
||||
|
||||
$dsn = sprintf('mysql:host=%s;dbname=%s;charset=utf8mb4', $host, $dbname);
|
||||
|
||||
$options = [
|
||||
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
|
||||
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
|
||||
PDO::ATTR_EMULATE_PREPARES => false,
|
||||
];
|
||||
|
||||
$pdo = new PDO($dsn, $user, $pass, $options);
|
||||
return $pdo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure we always have a friendly display name for the admin bar.
|
||||
*/
|
||||
if (empty($_SESSION['ADMIN_NAME'])) {
|
||||
$_SESSION['ADMIN_NAME'] = $_SESSION['admin_user']
|
||||
?? ($_SESSION['username'] ?? 'Administrator');
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper for formatting numbers consistently.
|
||||
*/
|
||||
function admin_format_number(mixed $value, int $decimals = 0): string
|
||||
{
|
||||
if ($value === null || $value === '') {
|
||||
$value = 0;
|
||||
}
|
||||
|
||||
return number_format((float) $value, $decimals);
|
||||
}
|
||||
|
||||
/**
|
||||
* Escape helper for HTML output.
|
||||
*/
|
||||
function admin_escape(string $value): string
|
||||
{
|
||||
return htmlspecialchars($value, ENT_QUOTES, 'UTF-8');
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience helper for date formatting.
|
||||
*/
|
||||
function admin_format_datetime(?string $date, string $format = 'M j, Y H:i'): string
|
||||
{
|
||||
if (!$date) {
|
||||
return 'N/A';
|
||||
}
|
||||
|
||||
try {
|
||||
return (new DateTime($date))->format($format);
|
||||
} catch (Exception) {
|
||||
return $date;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the initial token setup wizard has been completed.
|
||||
*/
|
||||
function admin_is_token_setup_complete(): bool
|
||||
{
|
||||
static $cached = null;
|
||||
if ($cached !== null) {
|
||||
return $cached;
|
||||
}
|
||||
|
||||
try {
|
||||
$pdo = admin_pdo();
|
||||
$stmt = $pdo->prepare("SELECT cfg_data FROM db_settings WHERE cfg_name = 'token_setup_complete' LIMIT 1");
|
||||
$stmt->execute();
|
||||
$value = $stmt->fetchColumn();
|
||||
$cached = $value === '1';
|
||||
return $cached;
|
||||
} catch (Exception) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark the token setup wizard as complete.
|
||||
*/
|
||||
function admin_mark_token_setup_complete(): void
|
||||
{
|
||||
$pdo = admin_pdo();
|
||||
$stmt = $pdo->prepare("
|
||||
INSERT INTO db_settings (cfg_name, cfg_data, cfg_info)
|
||||
VALUES ('token_setup_complete', '1', 'Flag indicating initial token setup completion')
|
||||
ON DUPLICATE KEY UPDATE cfg_data = '1', cfg_info = 'Flag indicating initial token setup completion'
|
||||
");
|
||||
$stmt->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate (or retrieve) a CSRF token for the given action.
|
||||
*/
|
||||
function admin_csrf_token(string $action): string
|
||||
{
|
||||
$key = "admin_csrf_{$action}";
|
||||
if (empty($_SESSION[$key])) {
|
||||
$_SESSION[$key] = bin2hex(random_bytes(32));
|
||||
}
|
||||
return $_SESSION[$key];
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate a CSRF token for the given action.
|
||||
*/
|
||||
function admin_validate_csrf(string $action, ?string $token): bool
|
||||
{
|
||||
$key = "admin_csrf_{$action}";
|
||||
if (empty($_SESSION[$key]) || !is_string($token)) {
|
||||
return false;
|
||||
}
|
||||
return hash_equals($_SESSION[$key], $token);
|
||||
}
|
||||
Reference in New Issue
Block a user