- Add flask-cors dependency and configure CORS with security best practices - Support configurable CORS origins via environment variables - Separate admin endpoint CORS configuration for enhanced security - Create comprehensive CORS configuration documentation - Add apiClient utility for CORS-aware frontend requests - Include CORS test page for validation - Update README with CORS configuration instructions 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
228 lines
8.5 KiB
HTML
228 lines
8.5 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>CORS Test for Talk2Me</title>
|
|
<style>
|
|
body {
|
|
font-family: Arial, sans-serif;
|
|
max-width: 800px;
|
|
margin: 50px auto;
|
|
padding: 20px;
|
|
}
|
|
.test-result {
|
|
margin: 10px 0;
|
|
padding: 10px;
|
|
border-radius: 5px;
|
|
}
|
|
.success {
|
|
background-color: #d4edda;
|
|
color: #155724;
|
|
border: 1px solid #c3e6cb;
|
|
}
|
|
.error {
|
|
background-color: #f8d7da;
|
|
color: #721c24;
|
|
border: 1px solid #f5c6cb;
|
|
}
|
|
button {
|
|
background-color: #007bff;
|
|
color: white;
|
|
padding: 10px 20px;
|
|
border: none;
|
|
border-radius: 5px;
|
|
cursor: pointer;
|
|
margin: 5px;
|
|
}
|
|
button:hover {
|
|
background-color: #0056b3;
|
|
}
|
|
input {
|
|
width: 100%;
|
|
padding: 10px;
|
|
margin: 10px 0;
|
|
border: 1px solid #ddd;
|
|
border-radius: 5px;
|
|
}
|
|
#results {
|
|
margin-top: 20px;
|
|
}
|
|
pre {
|
|
background-color: #f8f9fa;
|
|
padding: 10px;
|
|
border-radius: 5px;
|
|
overflow-x: auto;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<h1>CORS Test for Talk2Me API</h1>
|
|
|
|
<p>This page tests CORS configuration for the Talk2Me API. Open this file from a different origin (e.g., file:// or a different port) to test cross-origin requests.</p>
|
|
|
|
<div>
|
|
<label for="apiUrl">API Base URL:</label>
|
|
<input type="text" id="apiUrl" placeholder="http://localhost:5005" value="http://localhost:5005">
|
|
</div>
|
|
|
|
<h2>Tests:</h2>
|
|
|
|
<button onclick="testHealthEndpoint()">Test Health Endpoint</button>
|
|
<button onclick="testPreflightRequest()">Test Preflight Request</button>
|
|
<button onclick="testTranscribeEndpoint()">Test Transcribe Endpoint (OPTIONS)</button>
|
|
<button onclick="testWithCredentials()">Test With Credentials</button>
|
|
|
|
<div id="results"></div>
|
|
|
|
<script>
|
|
function addResult(test, success, message, details = null) {
|
|
const resultsDiv = document.getElementById('results');
|
|
const resultDiv = document.createElement('div');
|
|
resultDiv.className = `test-result ${success ? 'success' : 'error'}`;
|
|
|
|
let html = `<strong>${test}:</strong> ${message}`;
|
|
if (details) {
|
|
html += `<pre>${JSON.stringify(details, null, 2)}</pre>`;
|
|
}
|
|
resultDiv.innerHTML = html;
|
|
resultsDiv.appendChild(resultDiv);
|
|
}
|
|
|
|
function getApiUrl() {
|
|
return document.getElementById('apiUrl').value.trim();
|
|
}
|
|
|
|
async function testHealthEndpoint() {
|
|
const apiUrl = getApiUrl();
|
|
try {
|
|
const response = await fetch(`${apiUrl}/health`, {
|
|
method: 'GET',
|
|
mode: 'cors',
|
|
headers: {
|
|
'Origin': window.location.origin
|
|
}
|
|
});
|
|
|
|
const data = await response.json();
|
|
|
|
// Check CORS headers
|
|
const corsHeaders = {
|
|
'Access-Control-Allow-Origin': response.headers.get('Access-Control-Allow-Origin'),
|
|
'Access-Control-Allow-Credentials': response.headers.get('Access-Control-Allow-Credentials')
|
|
};
|
|
|
|
addResult('Health Endpoint GET', true, 'Request successful', {
|
|
status: response.status,
|
|
data: data,
|
|
corsHeaders: corsHeaders
|
|
});
|
|
} catch (error) {
|
|
addResult('Health Endpoint GET', false, error.message);
|
|
}
|
|
}
|
|
|
|
async function testPreflightRequest() {
|
|
const apiUrl = getApiUrl();
|
|
try {
|
|
const response = await fetch(`${apiUrl}/api/push-public-key`, {
|
|
method: 'OPTIONS',
|
|
mode: 'cors',
|
|
headers: {
|
|
'Origin': window.location.origin,
|
|
'Access-Control-Request-Method': 'GET',
|
|
'Access-Control-Request-Headers': 'content-type'
|
|
}
|
|
});
|
|
|
|
const corsHeaders = {
|
|
'Access-Control-Allow-Origin': response.headers.get('Access-Control-Allow-Origin'),
|
|
'Access-Control-Allow-Methods': response.headers.get('Access-Control-Allow-Methods'),
|
|
'Access-Control-Allow-Headers': response.headers.get('Access-Control-Allow-Headers'),
|
|
'Access-Control-Max-Age': response.headers.get('Access-Control-Max-Age')
|
|
};
|
|
|
|
addResult('Preflight Request', response.ok, `Status: ${response.status}`, corsHeaders);
|
|
} catch (error) {
|
|
addResult('Preflight Request', false, error.message);
|
|
}
|
|
}
|
|
|
|
async function testTranscribeEndpoint() {
|
|
const apiUrl = getApiUrl();
|
|
try {
|
|
const response = await fetch(`${apiUrl}/transcribe`, {
|
|
method: 'OPTIONS',
|
|
mode: 'cors',
|
|
headers: {
|
|
'Origin': window.location.origin,
|
|
'Access-Control-Request-Method': 'POST',
|
|
'Access-Control-Request-Headers': 'content-type'
|
|
}
|
|
});
|
|
|
|
const corsHeaders = {
|
|
'Access-Control-Allow-Origin': response.headers.get('Access-Control-Allow-Origin'),
|
|
'Access-Control-Allow-Methods': response.headers.get('Access-Control-Allow-Methods'),
|
|
'Access-Control-Allow-Headers': response.headers.get('Access-Control-Allow-Headers'),
|
|
'Access-Control-Allow-Credentials': response.headers.get('Access-Control-Allow-Credentials')
|
|
};
|
|
|
|
addResult('Transcribe Endpoint OPTIONS', response.ok, `Status: ${response.status}`, corsHeaders);
|
|
} catch (error) {
|
|
addResult('Transcribe Endpoint OPTIONS', false, error.message);
|
|
}
|
|
}
|
|
|
|
async function testWithCredentials() {
|
|
const apiUrl = getApiUrl();
|
|
try {
|
|
const response = await fetch(`${apiUrl}/health`, {
|
|
method: 'GET',
|
|
mode: 'cors',
|
|
credentials: 'include',
|
|
headers: {
|
|
'Origin': window.location.origin
|
|
}
|
|
});
|
|
|
|
const data = await response.json();
|
|
|
|
addResult('Request with Credentials', true, 'Request successful', {
|
|
status: response.status,
|
|
credentialsIncluded: true,
|
|
data: data
|
|
});
|
|
} catch (error) {
|
|
addResult('Request with Credentials', false, error.message);
|
|
}
|
|
}
|
|
|
|
// Clear results before running new tests
|
|
function clearResults() {
|
|
document.getElementById('results').innerHTML = '';
|
|
}
|
|
|
|
// Add event listeners
|
|
document.querySelectorAll('button').forEach(button => {
|
|
button.addEventListener('click', (e) => {
|
|
if (!e.target.textContent.includes('Test')) return;
|
|
clearResults();
|
|
});
|
|
});
|
|
|
|
// Show current origin
|
|
window.addEventListener('load', () => {
|
|
const info = document.createElement('div');
|
|
info.style.marginBottom = '20px';
|
|
info.style.padding = '10px';
|
|
info.style.backgroundColor = '#e9ecef';
|
|
info.style.borderRadius = '5px';
|
|
info.innerHTML = `<strong>Current Origin:</strong> ${window.location.origin}<br>
|
|
<strong>Protocol:</strong> ${window.location.protocol}<br>
|
|
<strong>Note:</strong> For effective CORS testing, open this file from a different origin than your API server.`;
|
|
document.body.insertBefore(info, document.querySelector('h2'));
|
|
});
|
|
</script>
|
|
</body>
|
|
</html> |