Synced files: - Quote wizard frontend (all components, hooks, types, config) - API updates (config, models, routers, schemas, services) - Client work (bg-builders, gurushow) - Scripts (BGB Lesley termination, CIPP, Datto, migration) - Temp files (Bardach contacts, VWP investigation, misc) - Credentials and session logs - Email service, PHP API, session logs Machine: ACG-M-L5090 Timestamp: 2026-03-10 19:11:00 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
149 lines
3.8 KiB
PHP
149 lines
3.8 KiB
PHP
<?php
|
|
/**
|
|
* Admin route handlers for quote management.
|
|
*
|
|
* All handlers require a valid API key in the Authorization header.
|
|
* Format: Authorization: Bearer {ADMIN_API_KEY}
|
|
*/
|
|
|
|
// Deny direct access
|
|
if (basename($_SERVER['SCRIPT_FILENAME'] ?? '') === basename(__FILE__)) {
|
|
http_response_code(403);
|
|
exit('Direct access denied.');
|
|
}
|
|
|
|
require_once __DIR__ . '/../helpers.php';
|
|
require_once __DIR__ . '/../db.php';
|
|
require_once __DIR__ . '/../services/quote_service.php';
|
|
require_once __DIR__ . '/../services/syncro_service.php';
|
|
|
|
/**
|
|
* Verify the admin API key from the Authorization header.
|
|
*
|
|
* Expects: Authorization: Bearer {api_key}
|
|
* Terminates with 401 if missing or invalid.
|
|
*/
|
|
function check_admin_auth(): void
|
|
{
|
|
$header = $_SERVER['HTTP_AUTHORIZATION']
|
|
?? $_SERVER['REDIRECT_HTTP_AUTHORIZATION']
|
|
?? '';
|
|
|
|
// Apache CGI/suPHP may strip Authorization header; check env var fallback
|
|
if (empty($header) && !empty(getenv('HTTP_AUTHORIZATION'))) {
|
|
$header = getenv('HTTP_AUTHORIZATION');
|
|
}
|
|
|
|
if (empty($header)) {
|
|
error_response('Authorization header required', 401);
|
|
}
|
|
|
|
// Extract bearer token
|
|
if (strpos($header, 'Bearer ') !== 0) {
|
|
error_response('Invalid authorization format. Expected: Bearer {api_key}', 401);
|
|
}
|
|
|
|
$token = substr($header, 7);
|
|
|
|
if (ADMIN_API_KEY === 'CHANGE_ME_PLACEHOLDER') {
|
|
app_log('WARNING', '[WARNING] Admin API key is not configured (still placeholder)');
|
|
error_response('Admin API key not configured on server', 500);
|
|
}
|
|
|
|
if (!hash_equals(ADMIN_API_KEY, $token)) {
|
|
app_log('WARNING', '[WARNING] Invalid admin API key attempt from ' . (get_client_ip() ?? 'unknown'));
|
|
error_response('Invalid API key', 401);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* GET /admin/quotes
|
|
*
|
|
* List quotes with pagination and optional filters.
|
|
* Query params: skip, limit, status, search
|
|
*/
|
|
function handle_list_quotes(): void
|
|
{
|
|
check_admin_auth();
|
|
$db = get_db();
|
|
|
|
$skip = max(0, (int)($_GET['skip'] ?? 0));
|
|
$limit = min(1000, max(1, (int)($_GET['limit'] ?? 100)));
|
|
$status = $_GET['status'] ?? null;
|
|
$search = $_GET['search'] ?? null;
|
|
|
|
// Validate status if provided
|
|
if ($status !== null && $status !== '' && !in_array($status, VALID_STATUSES, true)) {
|
|
error_response("Invalid status filter: {$status}", 400);
|
|
}
|
|
|
|
$result = list_quotes($db, $skip, $limit, $status, $search);
|
|
|
|
json_response([
|
|
'total' => $result['total'],
|
|
'skip' => $skip,
|
|
'limit' => $limit,
|
|
'quotes' => $result['quotes'],
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* GET /admin/quotes/stats
|
|
*
|
|
* Get dashboard statistics for quotes.
|
|
*/
|
|
function handle_get_stats(): void
|
|
{
|
|
check_admin_auth();
|
|
$db = get_db();
|
|
|
|
$stats = get_stats($db);
|
|
json_response($stats);
|
|
}
|
|
|
|
/**
|
|
* GET /admin/quotes/{id}
|
|
*
|
|
* Get a single quote by ID with items, activities, and notifications.
|
|
*/
|
|
function handle_admin_get_quote(string $quote_id): void
|
|
{
|
|
check_admin_auth();
|
|
$db = get_db();
|
|
|
|
$quote = get_quote_by_id($db, $quote_id);
|
|
$response = build_admin_quote_response($db, $quote);
|
|
json_response($response);
|
|
}
|
|
|
|
/**
|
|
* PUT /admin/quotes/{id}
|
|
*
|
|
* Update a quote's status and/or expiration (admin only).
|
|
*/
|
|
function handle_admin_update_quote(string $quote_id): void
|
|
{
|
|
check_admin_auth();
|
|
$data = get_json_body();
|
|
$db = get_db();
|
|
|
|
$quote = admin_update_quote($db, $quote_id, $data, 'admin');
|
|
$response = build_admin_quote_response($db, $quote);
|
|
json_response($response);
|
|
}
|
|
|
|
/**
|
|
* POST /admin/quotes/{id}/sync-syncro
|
|
*
|
|
* Trigger a SyncroRMM sync for a quote.
|
|
*/
|
|
function handle_sync_syncro(string $quote_id): void
|
|
{
|
|
check_admin_auth();
|
|
$db = get_db();
|
|
|
|
$quote = get_quote_by_id($db, $quote_id);
|
|
$result = sync_quote_to_syncro($db, $quote);
|
|
json_response($result);
|
|
}
|