opensearch-api 0.1.0

High-performance REST API gateway for OpenSearch with security, observability and multi-tenant support
<!DOCTYPE html>
<html lang="pt-BR">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>OpenSearch API - Interactive Playground</title>
    <style>
        * { margin: 0; padding: 0; box-sizing: border-box; }
        body {
            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            min-height: 100vh;
        }
        .container {
            max-width: 1400px;
            margin: 0 auto;
            padding: 20px;
        }
        header {
            background: rgba(255, 255, 255, 0.95);
            border-radius: 15px;
            padding: 20px;
            margin-bottom: 20px;
        }
        h1 { color: #667eea; }
        .playground {
            display: grid;
            grid-template-columns: 1fr 1fr;
            gap: 20px;
        }
        .panel {
            background: rgba(255, 255, 255, 0.95);
            border-radius: 15px;
            padding: 20px;
        }
        .controls {
            display: flex;
            gap: 10px;
            margin-bottom: 15px;
        }
        select, input {
            padding: 8px;
            border: 1px solid #ddd;
            border-radius: 5px;
        }
        textarea {
            width: 100%;
            height: 400px;
            padding: 10px;
            border: 1px solid #ddd;
            border-radius: 5px;
            font-family: 'Fira Code', monospace;
            font-size: 14px;
        }
        button {
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            color: white;
            border: none;
            padding: 10px 20px;
            border-radius: 5px;
            cursor: pointer;
            font-weight: 600;
        }
        button:hover {
            transform: translateY(-2px);
        }
        .response {
            background: #f8f9fa;
            border-radius: 5px;
            padding: 15px;
            margin-top: 15px;
            white-space: pre-wrap;
            font-family: 'Fira Code', monospace;
            font-size: 14px;
            max-height: 500px;
            overflow-y: auto;
        }
        .status {
            padding: 5px 10px;
            border-radius: 5px;
            font-size: 12px;
            font-weight: 600;
        }
        .status.success { background: #d4edda; color: #155724; }
        .status.error { background: #f8d7da; color: #721c24; }
        .examples {
            margin-top: 20px;
            padding: 15px;
            background: #f8f9fa;
            border-radius: 10px;
        }
        .example-btn {
            background: #6c757d;
            color: white;
            border: none;
            padding: 5px 10px;
            margin: 5px;
            border-radius: 5px;
            cursor: pointer;
            font-size: 12px;
        }
        .example-btn:hover {
            background: #5a6268;
        }
    </style>
</head>
<body>
    <div class="container">
        <header>
            <h1>🎮 API Playground</h1>
            <p>Teste interativo da OpenSearch API</p>
        </header>
        
        <div class="playground">
            <div class="panel">
                <h3>Request</h3>
                <div class="controls">
                    <select id="method">
                        <option>GET</option>
                        <option>POST</option>
                        <option>PUT</option>
                        <option>DELETE</option>
                    </select>
                    <input type="text" id="endpoint" placeholder="/search/logs-*?q=error" style="flex: 1;">
                    <button onclick="sendRequest()">Send</button>
                </div>
                <textarea id="body" placeholder="Request body (JSON)...">{
  "query": {
    "match": {
      "level": "ERROR"
    }
  }
}</textarea>
                <div class="controls">
                    <input type="text" id="apikey" placeholder="API Key: sk_live_..." style="flex: 1;">
                </div>
                
                <div class="examples">
                    <h4>Exemplos Rápidos:</h4>
                    <button class="example-btn" onclick="loadExample('search_logs')">Buscar Logs</button>
                    <button class="example-btn" onclick="loadExample('send_metrics')">Enviar Métricas</button>
                    <button class="example-btn" onclick="loadExample('trace')">Enviar Trace</button>
                    <button class="example-btn" onclick="loadExample('vector_search')">Busca Vetorial</button>
                </div>
            </div>
            
            <div class="panel">
                <h3>Response</h3>
                <div id="status"></div>
                <div id="response" class="response">Response will appear here...</div>
            </div>
        </div>
    </div>
    
    <script>
        const examples = {
            search_logs: {
                method: 'GET',
                endpoint: '/search/logs-*?q=level:ERROR&size=10',
                body: ''
            },
            send_metrics: {
                method: 'POST',
                endpoint: '/index/metrics-api-2024.01.01',
                body: JSON.stringify({
                    "@timestamp": new Date().toISOString(),
                    "service": "test-api",
                    "metrics": {
                        "cpu": {
                            "usage_percent": 45.2
                        },
                        "memory": {
                            "used_mb": 3200,
                            "total_mb": 8192
                        }
                    }
                }, null, 2)
            },
            trace: {
                method: 'POST',
                endpoint: '/index/traces-api-2024.01.01',
                body: JSON.stringify({
                    "@timestamp": new Date().toISOString(),
                    "trace_id": "4bf92f3577b34da6a3ce929d0e0e4736",
                    "span_id": "00f067aa0ba902b7",
                    "operation_name": "test.operation",
                    "service": "test-api",
                    "duration_ms": 123
                }, null, 2)
            },
            vector_search: {
                method: 'POST',
                endpoint: '/_search/vectors-*',
                body: JSON.stringify({
                    "size": 10,
                    "query": {
                        "knn": {
                            "embedding.vector": {
                                "vector": [0.1, 0.2, 0.3],
                                "k": 10
                            }
                        }
                    }
                }, null, 2)
            }
        };
        
        function loadExample(name) {
            const example = examples[name];
            document.getElementById('method').value = example.method;
            document.getElementById('endpoint').value = example.endpoint;
            document.getElementById('body').value = example.body;
        }
        
        async function sendRequest() {
            const method = document.getElementById('method').value;
            const endpoint = document.getElementById('endpoint').value;
            const body = document.getElementById('body').value;
            const apikey = document.getElementById('apikey').value;
            
            const options = {
                method: method,
                headers: {
                    'X-API-Key': apikey,
                    'Content-Type': 'application/json'
                }
            };
            
            if (method !== 'GET' && body) {
                try {
                    JSON.parse(body); // Validate JSON
                    options.body = body;
                } catch (e) {
                    document.getElementById('status').innerHTML = 
                        '<span class="status error">Invalid JSON</span>';
                    document.getElementById('response').textContent = 
                        'Error: Invalid JSON in request body\n' + e.message;
                    return;
                }
            }
            
            try {
                const baseUrl = window.location.origin;
                const response = await fetch(baseUrl + endpoint, options);
                const contentType = response.headers.get('content-type');
                let data;
                
                if (contentType && contentType.includes('application/json')) {
                    data = await response.json();
                } else {
                    data = await response.text();
                }
                
                document.getElementById('status').innerHTML = 
                    `<span class="status ${response.ok ? 'success' : 'error'}">
                        ${response.status} ${response.statusText}
                    </span>`;
                
                if (typeof data === 'object') {
                    document.getElementById('response').textContent = 
                        JSON.stringify(data, null, 2);
                } else {
                    document.getElementById('response').textContent = data;
                }
            } catch (error) {
                document.getElementById('status').innerHTML = 
                    '<span class="status error">Network Error</span>';
                document.getElementById('response').textContent = 
                    'Error: ' + error.message;
            }
        }
        
        // Load default API key from localStorage if available
        window.onload = function() {
            const savedKey = localStorage.getItem('apiKey');
            if (savedKey) {
                document.getElementById('apikey').value = savedKey;
            }
        };
        
        // Save API key to localStorage on change
        document.getElementById('apikey').addEventListener('change', function(e) {
            localStorage.setItem('apiKey', e.target.value);
        });
    </script>
</body>
</html>