/** * EU-Utility External API Test Client (JavaScript/Node.js) * ========================================================= * * Example JavaScript client for testing EU-Utility external API. * Supports REST API and webhook integrations. * * Usage: * node api_client_test.js --help * node api_client_test.js health * node api_client_test.js status * node api_client_test.js notify "Test Title" "Test Message" * node api_client_test.js search "ArMatrix" * node api_client_test.js webhook --url */ const http = require('http'); const https = require('https'); const { URL } = require('url'); class EUUtilityClient { constructor(host = '127.0.0.1', port = 8080, apiKey = null) { this.host = host; this.port = port; this.apiKey = apiKey; this.baseUrl = `http://${host}:${port}`; } _request(method, endpoint, data = null) { return new Promise((resolve, reject) => { const url = new URL(endpoint, this.baseUrl); const options = { method: method, headers: { 'Content-Type': 'application/json' } }; if (this.apiKey) { options.headers['X-API-Key'] = this.apiKey; } const req = http.request(url, options, (res) => { let responseData = ''; res.on('data', (chunk) => { responseData += chunk; }); res.on('end', () => { try { const data = responseData ? JSON.parse(responseData) : null; resolve({ status: res.statusCode, data: data }); } catch (e) { resolve({ status: res.statusCode, data: responseData }); } }); }); req.on('error', (error) => { reject(error); }); if (data) { req.write(JSON.stringify(data)); } req.end(); }); } async healthCheck() { return this._request('GET', '/health'); } async getStatus() { return this._request('GET', '/api/v1/status'); } async sendNotification(title, message, type = 'info') { return this._request('POST', '/api/v1/notify', { title: title, message: message, type: type }); } async searchNexus(query, entityType = 'items') { return this._request('GET', `/api/v1/search?q=${encodeURIComponent(query)}&type=${entityType}`); } async recordLoot(value, mob, items = []) { return this._request('POST', '/api/v1/loot', { value: value, mob: mob, items: items, timestamp: Date.now() }); } async getLootSession() { return this._request('GET', '/api/v1/loot/session'); } } async function sendDiscordWebhook(webhookUrl, content, embeds = null) { return new Promise((resolve, reject) => { const payload = { content: content }; if (embeds) { payload.embeds = embeds; } const url = new URL(webhookUrl); const options = { method: 'POST', headers: { 'Content-Type': 'application/json' } }; const req = https.request(url, options, (res) => { resolve(res.statusCode >= 200 && res.statusCode < 300); }); req.on('error', (error) => { reject(error); }); req.write(JSON.stringify(payload)); req.end(); }); } function validateWebhookPayload(payload) { if (typeof payload !== 'object' || payload === null) { return { valid: false, error: 'Payload must be an object' }; } if (!payload.content && !payload.embeds) { return { valid: false, error: 'Payload must contain content or embeds' }; } if (payload.content && payload.content.length > 2000) { return { valid: false, error: 'Content exceeds 2000 character limit' }; } if (payload.embeds) { if (!Array.isArray(payload.embeds)) { return { valid: false, error: 'Embeds must be an array' }; } if (payload.embeds.length > 10) { return { valid: false, error: 'Maximum 10 embeds allowed' }; } } return { valid: true, error: null }; } async function testAllEndpoints(client) { console.log('Testing EU-Utility External API...'); console.log('=' .repeat(50)); const tests = [ ['Health Check', () => client.healthCheck()], ['Get Status', () => client.getStatus()], ['Send Notification', () => client.sendNotification('Test', 'Hello from JS client')], ['Search Nexus', () => client.searchNexus('ArMatrix')], ['Record Loot', () => client.recordLoot(50.25, 'Atrox Young', ['Animal Oil'])], ['Get Loot Session', () => client.getLootSession()], ]; for (const [name, testFunc] of tests) { try { console.log(`\n${name}...`); const result = await testFunc(); const status = result.status >= 200 && result.status < 300 ? '✅' : '❌'; console.log(`${status} Status: ${result.status}`); console.log(` Response:`, result.data); } catch (error) { console.log(`❌ Error: ${error.message}`); } } console.log('\n' + '='.repeat(50)); console.log('Tests completed'); } // CLI handling function printHelp() { console.log(` EU-Utility External API Test Client (JavaScript) Usage: node api_client_test.js [options] Options: --host API host (default: 127.0.0.1) --port API port (default: 8080) --api-key API key for authentication Commands: health Check API health status Get EU-Utility status notify <msg> Send notification search <query> Search Nexus loot <value> <mob> Record loot global <value> <mob> Record global webhook --url <url> Send Discord webhook test Test all endpoints validate <payload> Validate webhook payload Examples: node api_client_test.js health node api_client_test.js notify "Test" "Hello World" node api_client_test.js search "ArMatrix" node api_client_test.js webhook --url https://discord.com/api/webhooks/... `); } async function main() { const args = process.argv.slice(2); if (args.length === 0 || args.includes('--help') || args.includes('-h')) { printHelp(); return; } // Parse options let host = '127.0.0.1'; let port = 8080; let apiKey = null; const hostIndex = args.indexOf('--host'); if (hostIndex !== -1) { host = args[hostIndex + 1]; args.splice(hostIndex, 2); } const portIndex = args.indexOf('--port'); if (portIndex !== -1) { port = parseInt(args[portIndex + 1]); args.splice(portIndex, 2); } const keyIndex = args.indexOf('--api-key'); if (keyIndex !== -1) { apiKey = args[keyIndex + 1]; args.splice(keyIndex, 2); } const command = args[0]; const client = new EUUtilityClient(host, port, apiKey); try { switch (command) { case 'health': { const result = await client.healthCheck(); console.log(JSON.stringify(result, null, 2)); break; } case 'status': { const result = await client.getStatus(); console.log(JSON.stringify(result, null, 2)); break; } case 'notify': { if (args.length < 3) { console.error('Usage: notify <title> <message>'); process.exit(1); } const result = await client.sendNotification(args[1], args[2]); console.log(JSON.stringify(result, null, 2)); break; } case 'search': { if (args.length < 2) { console.error('Usage: search <query>'); process.exit(1); } const result = await client.searchNexus(args[1]); console.log(JSON.stringify(result, null, 2)); break; } case 'loot': { if (args.length < 3) { console.error('Usage: loot <value> <mob>'); process.exit(1); } const result = await client.recordLoot(parseFloat(args[1]), args[2]); console.log(JSON.stringify(result, null, 2)); break; } case 'global': { if (args.length < 3) { console.error('Usage: global <value> <mob>'); process.exit(1); } const result = await client.recordLoot(parseFloat(args[1]), args[2]); console.log(JSON.stringify(result, null, 2)); break; } case 'webhook': { const urlIndex = args.indexOf('--url'); if (urlIndex === -1 || !args[urlIndex + 1]) { console.error('Usage: webhook --url <webhook_url>'); process.exit(1); } const contentIndex = args.indexOf('--content'); const content = contentIndex !== -1 ? args[contentIndex + 1] : 'Test from EU-Utility'; const success = await sendDiscordWebhook(args[urlIndex + 1], content); console.log(success ? '✅ Sent' : '❌ Failed'); break; } case 'test': { await testAllEndpoints(client); break; } case 'validate': { if (args.length < 2) { console.error('Usage: validate <json_payload>'); process.exit(1); } try { const payload = JSON.parse(args[1]); const validation = validateWebhookPayload(payload); console.log(validation.valid ? '✅ Valid' : `❌ ${validation.error}`); } catch (e) { console.error(`❌ Invalid JSON: ${e.message}`); } break; } default: console.error(`Unknown command: ${command}`); printHelp(); process.exit(1); } } catch (error) { console.error(`Error: ${error.message}`); process.exit(1); } } main();