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>
165 lines
5.5 KiB
PHP
165 lines
5.5 KiB
PHP
<?php
|
|
/**
|
|
* Front controller / router for the MSP Quote Wizard PHP API.
|
|
*
|
|
* All requests are routed here via .htaccess. Parses the URI and method,
|
|
* emits CORS headers, then dispatches to the appropriate route handler.
|
|
*
|
|
* Route map:
|
|
* POST /quotes -> create quote
|
|
* GET /quotes/{token} -> get quote by token
|
|
* PUT /quotes/{token} -> update quote
|
|
* POST /quotes/{token}/items -> add item
|
|
* DELETE /quotes/{token}/items/{id} -> remove item
|
|
* POST /quotes/{token}/submit -> submit quote
|
|
* GET /admin/quotes -> list quotes (auth)
|
|
* GET /admin/quotes/stats -> get stats (auth)
|
|
* GET /admin/quotes/{id} -> get quote by ID (auth)
|
|
* PUT /admin/quotes/{id} -> update quote status (auth)
|
|
* POST /admin/quotes/{id}/sync-syncro -> sync to Syncro (auth)
|
|
*/
|
|
|
|
// Error reporting: log only, never display to client
|
|
ini_set('display_errors', '0');
|
|
error_reporting(E_ALL);
|
|
|
|
require_once __DIR__ . '/helpers.php';
|
|
|
|
// Emit CORS headers on every request (handles OPTIONS preflight too)
|
|
cors_headers();
|
|
|
|
// Parse request
|
|
$method = $_SERVER['REQUEST_METHOD'];
|
|
|
|
// Get the path relative to the API directory
|
|
// Strip the script directory from REQUEST_URI to get the route path
|
|
$request_uri = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
|
|
|
|
// Determine the base path (the directory where index.php lives)
|
|
$script_dir = dirname($_SERVER['SCRIPT_NAME']);
|
|
if ($script_dir !== '/' && $script_dir !== '\\') {
|
|
$path = substr($request_uri, strlen($script_dir));
|
|
} else {
|
|
$path = $request_uri;
|
|
}
|
|
|
|
// Normalize: ensure leading slash, remove trailing slash (except root)
|
|
$path = '/' . ltrim($path, '/');
|
|
if ($path !== '/' && substr($path, -1) === '/') {
|
|
$path = rtrim($path, '/');
|
|
}
|
|
|
|
// Split path into segments for matching
|
|
$segments = array_values(array_filter(explode('/', $path), function ($s) {
|
|
return $s !== '';
|
|
}));
|
|
$seg_count = count($segments);
|
|
|
|
// --------------------------------------------------------------------------
|
|
// Route dispatch
|
|
// --------------------------------------------------------------------------
|
|
|
|
// -- Public quote routes: /quotes/... --
|
|
if ($seg_count >= 1 && $segments[0] === 'quotes') {
|
|
|
|
require_once __DIR__ . '/routes/quotes.php';
|
|
|
|
// POST /quotes -> create
|
|
if ($seg_count === 1 && $method === 'POST') {
|
|
handle_create_quote();
|
|
}
|
|
|
|
// GET /quotes/{token} -> get
|
|
if ($seg_count === 2 && $method === 'GET') {
|
|
handle_get_quote($segments[1]);
|
|
}
|
|
|
|
// PUT /quotes/{token} -> update
|
|
if ($seg_count === 2 && $method === 'PUT') {
|
|
handle_update_quote($segments[1]);
|
|
}
|
|
|
|
// POST /quotes/{token}/items -> add item
|
|
if ($seg_count === 3 && $segments[2] === 'items' && $method === 'POST') {
|
|
handle_add_item($segments[1]);
|
|
}
|
|
|
|
// DELETE /quotes/{token}/items/{id} -> remove item
|
|
if ($seg_count === 4 && $segments[2] === 'items' && $method === 'DELETE') {
|
|
handle_remove_item($segments[1], $segments[3]);
|
|
}
|
|
|
|
// POST /quotes/{token}/submit -> submit
|
|
if ($seg_count === 3 && $segments[2] === 'submit' && $method === 'POST') {
|
|
handle_submit_quote($segments[1]);
|
|
}
|
|
|
|
// If we got here with a quotes path but no match, 404
|
|
error_response('Not found', 404);
|
|
}
|
|
|
|
// -- Admin routes: /admin/quotes/... --
|
|
if ($seg_count >= 2 && $segments[0] === 'admin' && $segments[1] === 'quotes') {
|
|
|
|
require_once __DIR__ . '/routes/admin.php';
|
|
|
|
// GET /admin/quotes -> list
|
|
if ($seg_count === 2 && $method === 'GET') {
|
|
handle_list_quotes();
|
|
}
|
|
|
|
// GET /admin/quotes/stats -> stats
|
|
if ($seg_count === 3 && $segments[2] === 'stats' && $method === 'GET') {
|
|
handle_get_stats();
|
|
}
|
|
|
|
// GET /admin/quotes/{id} -> get by ID
|
|
if ($seg_count === 3 && $segments[2] !== 'stats' && $method === 'GET') {
|
|
handle_admin_get_quote($segments[2]);
|
|
}
|
|
|
|
// PUT /admin/quotes/{id} -> admin update
|
|
if ($seg_count === 3 && $method === 'PUT') {
|
|
handle_admin_update_quote($segments[2]);
|
|
}
|
|
|
|
// POST /admin/quotes/{id}/sync-syncro -> syncro sync
|
|
if ($seg_count === 4 && $segments[3] === 'sync-syncro' && $method === 'POST') {
|
|
handle_sync_syncro($segments[2]);
|
|
}
|
|
|
|
// If we got here with an admin path but no match, 404
|
|
error_response('Not found', 404);
|
|
}
|
|
|
|
// --------------------------------------------------------------------------
|
|
// Health check: GET /health
|
|
// --------------------------------------------------------------------------
|
|
if ($seg_count === 1 && $segments[0] === 'health' && $method === 'GET') {
|
|
// Quick DB connectivity check
|
|
try {
|
|
require_once __DIR__ . '/db.php';
|
|
$db = get_db();
|
|
$db->query('SELECT 1');
|
|
json_response(['status' => 'ok', 'database' => 'connected']);
|
|
} catch (\Throwable $e) {
|
|
json_response(['status' => 'error', 'database' => 'disconnected'], 503);
|
|
}
|
|
}
|
|
|
|
// --------------------------------------------------------------------------
|
|
// Root: GET /
|
|
// --------------------------------------------------------------------------
|
|
if ($seg_count === 0 && $method === 'GET') {
|
|
json_response([
|
|
'service' => 'MSP Quote Wizard API',
|
|
'version' => '1.0.0',
|
|
'status' => 'running',
|
|
]);
|
|
}
|
|
|
|
// --------------------------------------------------------------------------
|
|
// 404 fallback
|
|
// --------------------------------------------------------------------------
|
|
error_response('Not found', 404);
|