2e450dc01d
P1-A: Fix CSP - add fonts.googleapis.com to style-src, fonts.gstatic.com to font-src
P1-B: CSRF token rotation - add rotateToken() to CsrfMiddleware; bootstrap.php rotates
after successful validation and stores in $GLOBALS['_new_csrf_token']; add
apiRespond() helper to append token to responses; lt.api interceptor in
layout_footer.php auto-updates window.CSRF_TOKEN from responses
P1-C: Styled 403/404 error views with TDS layout instead of raw text; index.php now
uses requireAdmin() helper eliminating 7 duplicated guard blocks (P3-D)
P2-A: Remove duplicate JS-generated keyboard help modal from keyboard-shortcuts.js;
'?' key now routes to static #lt-keys-help modal in footer
P2-B: Asset versioning driven by config ASSET_VERSION key; base.css and base.js get
?v= cache-busting in layout_header.php
P2-C: Add data-theme="dark" to <html> tag to prevent FOUC on light-mode users
P2-E: Escape status value in dashboard.js hover preview class attribute via lt.escHtml()
P2-F: Replace bespoke showLoadingOverlay() with lt-spinner / lt-loading-text from
base.css; add .lt-loading-overlay wrapper CSS to dashboard.css
P2-G: Add keyboard-shortcuts.js to all 7 admin views so J/K nav and ? help work
P3-A: APP_NAME, APP_SUBTITLE, APP_VERSION driven from config.php; layout header/footer
use config values instead of hardcoded strings
P3-G: Replace custom initTableSorting() with lt.sortTable.init() which manages aria-sort
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
64 lines
2.0 KiB
PHP
64 lines
2.0 KiB
PHP
<?php
|
|
/**
|
|
* API Bootstrap - Common setup for API endpoints
|
|
*
|
|
* Provides: $conn, $currentUser, $userId, $isAdmin
|
|
*
|
|
* Usage:
|
|
* require_once __DIR__ . '/bootstrap.php';
|
|
* // $conn, $currentUser, $userId, $isAdmin are now available
|
|
*/
|
|
|
|
ini_set('display_errors', 0);
|
|
error_reporting(E_ALL);
|
|
|
|
// Rate limiting (also starts session)
|
|
require_once dirname(__DIR__) . '/middleware/RateLimitMiddleware.php';
|
|
RateLimitMiddleware::apply('api');
|
|
|
|
// Config and database
|
|
require_once dirname(__DIR__) . '/config/config.php';
|
|
require_once dirname(__DIR__) . '/helpers/Database.php';
|
|
|
|
// Authentication check
|
|
if (!isset($_SESSION['user']) || !isset($_SESSION['user']['user_id'])) {
|
|
http_response_code(401);
|
|
header('Content-Type: application/json');
|
|
echo json_encode(['success' => false, 'error' => 'Authentication required']);
|
|
exit;
|
|
}
|
|
|
|
// CSRF protection for write requests
|
|
if (in_array($_SERVER['REQUEST_METHOD'], ['POST', 'PUT', 'DELETE'])) {
|
|
require_once dirname(__DIR__) . '/middleware/CsrfMiddleware.php';
|
|
$csrfToken = $_SERVER['HTTP_X_CSRF_TOKEN'] ?? '';
|
|
if (!CsrfMiddleware::validateToken($csrfToken)) {
|
|
http_response_code(403);
|
|
header('Content-Type: application/json');
|
|
echo json_encode(['success' => false, 'error' => 'Invalid CSRF token']);
|
|
exit;
|
|
}
|
|
// Rotate token after successful validation; endpoints include it in their JSON response
|
|
$GLOBALS['_new_csrf_token'] = CsrfMiddleware::rotateToken();
|
|
}
|
|
|
|
header('Content-Type: application/json');
|
|
|
|
// Common variables
|
|
$currentUser = $_SESSION['user'];
|
|
$userId = $currentUser['user_id'];
|
|
$isAdmin = $currentUser['is_admin'] ?? false;
|
|
$conn = Database::getConnection();
|
|
|
|
/**
|
|
* Output a JSON response, appending the rotated CSRF token so the
|
|
* client-side lt.api interceptor can update window.CSRF_TOKEN.
|
|
*/
|
|
function apiRespond(array $data): void {
|
|
if (!empty($GLOBALS['_new_csrf_token'])) {
|
|
$data['csrf_token'] = $GLOBALS['_new_csrf_token'];
|
|
}
|
|
echo json_encode($data);
|
|
exit;
|
|
}
|