/** * Pennyzo General API Status Checker (Extended Version) * * This script checks the status of all primary endpoints of the Pennyzo General API, * logs the results to the console, and optionally saves a detailed report to a file. * * Features: * - Tests both public and authenticated endpoints * - Provides detailed error information * - Generates a report with timestamp * - Saves results to a file * - Supports command line arguments for customization */ // Load environment variables from .env file require('dotenv').config(); const axios = require('axios'); const chalk = require('chalk'); const fs = require('fs'); const path = require('path'); // Parse command line arguments const args = process.argv.slice(2); const saveReport = !args.includes('--no-report'); const verbose = args.includes('--verbose'); const useRealCredentials = args.includes('--use-real-credentials'); // Define the base URL based on environment const BASE_URL = process.env.VITE_API_BASE_URL || process.env.API_BASE_URL || (process.env.NODE_ENV === 'production' ? 'https://api.pennyzo.com/api/v1/general' : 'http://localhost:8000'); // Test credentials const TEST_CREDENTIALS = { email: useRealCredentials ? process.env.PENNYZO_TEST_EMAIL || 'test@example.com' : 'test@example.com', password: useRealCredentials ? process.env.PENNYZO_TEST_PASSWORD || 'Password123!' : 'Password123!' }; // Store the access token for authenticated requests let accessToken = null; // Store all results for reporting const results = []; const startTime = new Date(); // Helper function to log API call results function logResult(endpoint, method, status, statusCode, responseTime, data = null, error = null) { const statusText = status === 'SUCCESS' ? chalk.green(status) : chalk.red(status); const statusCodeText = statusCode ? `(${statusCode})` : ''; const responseTimeText = responseTime ? `[${responseTime}ms]` : ''; console.log(`${chalk.yellow(method)} ${chalk.blue(endpoint)}: ${statusText} ${statusCodeText} ${responseTimeText}`); if (error) { console.log(` Error: ${chalk.red(error.message)}`); if (error.response?.data && verbose) { console.log(` Details: ${JSON.stringify(error.response.data, null, 2)}`); } } else if (data && verbose) { const dataPreview = JSON.stringify(data).substring(0, 100) + (JSON.stringify(data).length > 100 ? '...' : ''); console.log(` Data: ${dataPreview}`); } // Store result for reporting results.push({ endpoint, method, status, statusCode: statusCode || 'N/A', responseTime, timestamp: new Date().toISOString(), error: error ? { message: error.message, details: error.response?.data || null } : null, data: data && verbose ? data : null }); } // Helper function to make API calls and log results async function callApi(endpoint, method = 'GET', data = null, authenticated = false) { const url = `${BASE_URL}${endpoint}`; const config = {}; if (authenticated && accessToken) { config.headers = { 'Authorization': `Bearer ${accessToken}` }; } // Special handling for form-urlencoded content type (login endpoint) if (endpoint === '/auth/login') { config.headers = { 'Content-Type': 'application/x-www-form-urlencoded' }; // Convert data to URLSearchParams for login endpoint if (data) { const params = new URLSearchParams(); params.append('username', data.email); // API uses username field for email params.append('password', data.password); data = params; } } const startTime = Date.now(); try { let response; switch (method) { case 'GET': response = await axios.get(url, config); break; case 'POST': response = await axios.post(url, data, config); break; default: throw new Error(`Unsupported method: ${method}`); } const responseTime = Date.now() - startTime; logResult(endpoint, method, 'SUCCESS', response.status, responseTime, response.data); return { success: true, data: response.data, responseTime }; } catch (error) { const responseTime = Date.now() - startTime; const statusCode = error.response?.status || 'N/A'; logResult(endpoint, method, 'FAILED', statusCode, responseTime, null, error); return { success: false, error, responseTime }; } } // Function to generate and save a report function saveReportToFile() { const endTime = new Date(); const duration = (endTime - startTime) / 1000; // in seconds // Count successes and failures const successCount = results.filter(r => r.status === 'SUCCESS').length; const failureCount = results.filter(r => r.status === 'FAILED').length; const report = { title: 'Pennyzo API Status Check Report', timestamp: endTime.toISOString(), duration: `${duration.toFixed(2)} seconds`, summary: { totalEndpoints: results.length, successful: successCount, failed: failureCount, successRate: `${((successCount / results.length) * 100).toFixed(1)}%` }, baseUrl: BASE_URL, results: results }; const reportDir = path.join(__dirname, '../reports'); if (!fs.existsSync(reportDir)) { fs.mkdirSync(reportDir, { recursive: true }); } const reportPath = path.join(reportDir, `api-status-${endTime.toISOString().replace(/:/g, '-')}.json`); fs.writeFileSync(reportPath, JSON.stringify(report, null, 2)); console.log(chalk.bold(`\nReport saved to: ${reportPath}`)); } // Main function to run the API checks async function checkApiStatus() { console.log(chalk.bold('\n=== Pennyzo General API Status Checker ===\n')); console.log(`Timestamp: ${startTime.toISOString()}`); console.log(`Base URL: ${BASE_URL}`); console.log(`Save Report: ${saveReport ? 'Yes' : 'No'}`); console.log(`Verbose Mode: ${verbose ? 'Yes' : 'No'}`); console.log(`Using Real Credentials: ${useRealCredentials ? 'Yes' : 'No'}\n`); console.log(chalk.bold('\n--- Testing Public Endpoints ---\n')); // Test system health endpoint const healthResult = await callApi('/system/health'); if (healthResult.success) { console.log(` API Status: ${healthResult.data.status}`); console.log(` Environment: ${healthResult.data.environment}`); console.log(` Version: ${healthResult.data.version}`); console.log(` Message: ${healthResult.data.message}\n`); } // Test registration company identity rules endpoint await callApi('/registration/company/identity-rules'); console.log(chalk.bold('\n--- Testing Authentication Flow ---\n')); // Try to authenticate with test credentials const loginResult = await callApi('/auth/login', 'POST', TEST_CREDENTIALS); if (loginResult.success) { accessToken = loginResult.data.access_token; console.log(chalk.green(' Authentication successful. Access token received.')); console.log(` Token type: ${loginResult.data.token_type}`); if (loginResult.data.expires_in) { console.log(` Token expires in: ${loginResult.data.expires_in} seconds\n`); } console.log(chalk.bold('\n--- Testing Authenticated Endpoints ---\n')); // Test authenticated endpoints await callApi('/auth/me', 'GET', null, true); await callApi('/users/profile', 'GET', null, true); await callApi('/users/stats', 'GET', null, true); await callApi('/users/membership-info', 'GET', null, true); } else { console.log(chalk.red(' Authentication failed. Cannot test authenticated endpoints.')); console.log(' Note: This is expected if using placeholder credentials.'); console.log(' To test authenticated endpoints, use the --use-real-credentials flag'); console.log(' and set PENNYZO_TEST_EMAIL and PENNYZO_TEST_PASSWORD environment variables.\n'); } // Summary const successCount = results.filter(r => r.status === 'SUCCESS').length; const failureCount = results.filter(r => r.status === 'FAILED').length; console.log(chalk.bold('\n=== API Status Check Summary ===\n')); console.log(`Total endpoints checked: ${results.length}`); console.log(`Successful: ${chalk.green(successCount)}`); console.log(`Failed: ${chalk.red(failureCount)}`); console.log(`Success rate: ${((successCount / results.length) * 100).toFixed(1)}%`); console.log(`Total time: ${((new Date() - startTime) / 1000).toFixed(2)} seconds\n`); // Save report if enabled if (saveReport) { saveReportToFile(); } console.log(chalk.bold('=== API Status Check Complete ===\n')); } // Execute the API checker checkApiStatus().catch(error => { console.error(chalk.red('\nUnexpected error during API status check:')); console.error(error); });