Files
rspade_system/app/RSpade/Commands/Rsx/Ajax_Debug_Command.php
root f6fac6c4bc Fix bin/publish: copy docs.dist from project root
Fix bin/publish: use correct .env path for rspade_system
Fix bin/publish script: prevent grep exit code 1 from terminating script

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-21 02:08:33 +00:00

224 lines
8.3 KiB
PHP
Executable File

<?php
/**
* CODING CONVENTION:
* This file follows the coding convention where variable_names and function_names
* use snake_case (underscore_wherever_possible).
*/
namespace App\RSpade\Commands\Rsx;
use Illuminate\Console\Command;
use App\RSpade\Core\Ajax\Ajax;
use App\RSpade\Core\Ajax\Exceptions\AjaxAuthRequiredException;
use App\RSpade\Core\Ajax\Exceptions\AjaxUnauthorizedException;
use App\RSpade\Core\Ajax\Exceptions\AjaxFormErrorException;
use App\RSpade\Core\Ajax\Exceptions\AjaxFatalErrorException;
use App\RSpade\Core\Session\RsxSession;
use App\RSpade\Core\Debug\Debugger;
/**
* RSX Ajax Debug Command
* =======================
*
* PURPOSE:
* This command allows testing Internal API (Ajax) calls from the command line.
* It sets up session context (user_id, site_id) and executes Ajax::call() with
* the specified controller, action, and parameters.
*
* HOW IT WORKS:
* 1. Rotates logs for clean slate debugging
* 2. Sets user_id and/or site_id in RsxSession if provided
* 3. Calls Ajax::call() with the specified controller and action
* 4. Outputs pretty-printed JSON response
* 5. Handles special response types (AuthRequired, FormError, etc.)
* 6. Rotates logs again after test for clean slate on next run
*
* KEY FEATURES:
* - User context: Use --user-id to test as any user
* - Site context: Use --site-id to test with specific site
* - JSON parameters: Pass complex data structures as JSON
* - Pretty output: Responses are formatted for readability
* - Form error handling: Special formatting for validation errors
* - Log integration: Automatic log rotation for clean testing
*
* USAGE EXAMPLES:
* php artisan rsx:ajax:debug User_Api get_profile # Basic call
* php artisan rsx:ajax:debug User_Api update --args='{"name":"John"}' # With params
* php artisan rsx:ajax:debug Admin_Api list_users --user-id=1 # As user 1
* php artisan rsx:ajax:debug Site_Api get_stats --site-id=5 # With site 5
* php artisan rsx:ajax:debug Auth_Api login --args='{"email":"test@example.com","password":"secret"}'
*
* OUTPUT FORMAT:
* - Success: Pretty-printed JSON response
* - Auth Required: Shows authentication error with login URL
* - Unauthorized: Shows authorization error message
* - Form Error: Shows validation errors with field details
* - Fatal Error: Shows error message and stack trace
* - Invalid controller/action: Clear error message
*
* SECURITY:
* - Only available in local/development/testing environments
* - Throws fatal error if attempted in production
* - Bypasses normal HTTP authentication for testing
*/
class Ajax_Debug_Command extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'rsx:ajax:debug
{controller : The RSX controller name (e.g., User_Api, Admin_Api)}
{action : The action/method name (e.g., get_profile, update)}
{--args= : JSON-encoded arguments to pass to the action}
{--user-id= : Set user ID for session context}
{--site-id= : Set site ID for session context}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Debug Internal API (Ajax) calls from command line - executes Ajax::call() with session context (development only)';
/**
* Execute the console command.
*/
public function handle()
{
// Check environment - throw fatal error in production
if (app()->environment('production')) {
throw new \RuntimeException('FATAL: rsx:ajax:debug command is not available in production environment. This is a development-only debugging tool.');
}
// Get command arguments
$controller = $this->argument('controller');
$action = $this->argument('action');
// Parse arguments if provided
$args = [];
if ($this->option('args')) {
$args_json = $this->option('args');
$args = json_decode($args_json, true);
if (json_last_error() !== JSON_ERROR_NONE) {
$this->error('❌ Invalid JSON in --args parameter: ' . json_last_error_msg());
return 1;
}
}
// Get user and site IDs
$user_id = $this->option('user-id');
$site_id = $this->option('site-id');
// Rotate logs before test to ensure clean slate
Debugger::logrotate();
// Set session context if provided
if ($user_id !== null) {
RsxSession::set_user_id((int)$user_id);
$this->info("✓ Set user_id to {$user_id}");
}
if ($site_id !== null) {
RsxSession::set_site_id((int)$site_id);
$this->info("✓ Set site_id to {$site_id}");
}
// Show what we're calling
$this->info("");
$this->info("Calling: {$controller}::{$action}");
if (!empty($args)) {
$this->info("Arguments: " . json_encode($args, JSON_PRETTY_PRINT));
}
$this->info("");
$this->line("─────────────────────────────────────────");
$this->info("");
try {
// Call the Ajax method
$response = Ajax::internal($controller, $action, $args);
// Output successful response
$this->info("✅ Success Response:");
$this->info("");
// Pretty print the JSON response
$json_output = json_encode($response, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
$this->line($json_output);
} catch (AjaxAuthRequiredException $e) {
// Handle auth required response
$this->error("🔒 Authentication Required");
$this->error("");
$this->error("Login URL: " . $e->getMessage());
return 1;
} catch (AjaxUnauthorizedException $e) {
// Handle unauthorized response
$this->error("⛔ Unauthorized");
$this->error("");
$this->error("Message: " . $e->getMessage());
return 1;
} catch (AjaxFormErrorException $e) {
// Handle form error response - format like /_ajax route
$this->error("❌ Form Validation Error");
$this->error("");
$details = $e->get_details();
// Format the response like the /_ajax route does
$formatted_response = [
'success' => false,
'error' => $e->getMessage()
];
// Add any additional details from the exception
if (!empty($details)) {
foreach ($details as $key => $value) {
if ($key !== 'message') {
$formatted_response[$key] = $value;
}
}
}
$json_output = json_encode($formatted_response, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
$this->line($json_output);
return 1;
} catch (AjaxFatalErrorException $e) {
// Handle fatal error response
$this->error("💀 Fatal Error");
$this->error("");
$this->error("Message: " . $e->getMessage());
$this->error("");
$this->error("Stack Trace:");
$this->line($e->getTraceAsString());
return 1;
} catch (\InvalidArgumentException $e) {
// Handle invalid controller/action
$this->error("❌ Invalid Request");
$this->error("");
$this->error($e->getMessage());
return 1;
} catch (\Exception $e) {
// Handle unexpected exceptions
$this->error("💥 Unexpected Error");
$this->error("");
$this->error("Exception: " . get_class($e));
$this->error("Message: " . $e->getMessage());
$this->error("");
$this->error("Stack Trace:");
$this->line($e->getTraceAsString());
return 1;
}
// Rotate logs after test to clean slate for next run
Debugger::logrotate();
return 0;
}
}