sync: Auto-sync from ACG-M-L5090 at 2026-03-10 19:11:00
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>
This commit is contained in:
164
projects/msp-tools/quote-wizard/php-api/api/index.php
Normal file
164
projects/msp-tools/quote-wizard/php-api/api/index.php
Normal file
@@ -0,0 +1,164 @@
|
||||
<?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);
|
||||
Reference in New Issue
Block a user