fix: route TTS through PHP proxy to bypass mixed-content/CORS
This commit is contained in:
@@ -190,6 +190,9 @@ try {
|
|||||||
case 'chat_clear':
|
case 'chat_clear':
|
||||||
chatClear($db);
|
chatClear($db);
|
||||||
break;
|
break;
|
||||||
|
case 'tts_proxy':
|
||||||
|
ttsProxy();
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
http_response_code(404);
|
http_response_code(404);
|
||||||
@@ -201,6 +204,55 @@ try {
|
|||||||
}
|
}
|
||||||
endif; // end !CRON_MODE
|
endif; // end !CRON_MODE
|
||||||
|
|
||||||
|
// ===== TTS PROXY =====
|
||||||
|
function ttsProxy() {
|
||||||
|
$body = json_decode(file_get_contents('php://input'), true);
|
||||||
|
$url = isset($body['url']) ? trim($body['url']) : '';
|
||||||
|
$method = isset($body['method']) ? strtoupper(trim($body['method'])) : 'POST';
|
||||||
|
$headers = isset($body['headers']) && is_array($body['headers']) ? $body['headers'] : [];
|
||||||
|
$payload = isset($body['payload']) ? $body['payload'] : '';
|
||||||
|
$contentType = '';
|
||||||
|
foreach ($headers as $k => $v) {
|
||||||
|
if (strtolower($k) === 'content-type') { $contentType = $v; break; }
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$url || !preg_match('/^https?:\/\/.+/', $url)) {
|
||||||
|
http_response_code(400);
|
||||||
|
echo json_encode(['error' => 'URL non valido']);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$curlHeaders = [];
|
||||||
|
foreach ($headers as $k => $v) {
|
||||||
|
$curlHeaders[] = "$k: $v";
|
||||||
|
}
|
||||||
|
|
||||||
|
$ch = curl_init($url);
|
||||||
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||||
|
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method);
|
||||||
|
if ($method !== 'GET' && $payload !== '') {
|
||||||
|
curl_setopt($ch, CURLOPT_POSTFIELDS, $payload);
|
||||||
|
}
|
||||||
|
if ($curlHeaders) {
|
||||||
|
curl_setopt($ch, CURLOPT_HTTPHEADER, $curlHeaders);
|
||||||
|
}
|
||||||
|
curl_setopt($ch, CURLOPT_TIMEOUT, 10);
|
||||||
|
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // allow self-signed certs on local network
|
||||||
|
$response = curl_exec($ch);
|
||||||
|
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||||
|
$curlErr = curl_error($ch);
|
||||||
|
curl_close($ch);
|
||||||
|
|
||||||
|
if ($curlErr) {
|
||||||
|
http_response_code(502);
|
||||||
|
echo json_encode(['error' => 'cURL error: ' . $curlErr]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
http_response_code($httpCode ?: 200);
|
||||||
|
echo json_encode(['status' => $httpCode, 'body' => $response]);
|
||||||
|
}
|
||||||
|
|
||||||
// ===== CLIENT LOG =====
|
// ===== CLIENT LOG =====
|
||||||
|
|
||||||
function clientLog(): void {
|
function clientLog(): void {
|
||||||
|
|||||||
+23
-6
@@ -6655,13 +6655,27 @@ function _buildTtsRequest(text, s) {
|
|||||||
return { url, method, headers, body };
|
return { url, method, headers, body };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function _ttsViaProxy(req) {
|
||||||
|
// Route through server-side proxy to avoid mixed-content / CORS issues
|
||||||
|
return fetch('api/index.php?action=tts_proxy', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({
|
||||||
|
url: req.url,
|
||||||
|
method: req.method,
|
||||||
|
headers: req.headers,
|
||||||
|
payload: req.body
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
async function speakCookingStep(text) {
|
async function speakCookingStep(text) {
|
||||||
if (!text) return;
|
if (!text) return;
|
||||||
const s = getSettings();
|
const s = getSettings();
|
||||||
if (!s.tts_enabled) return;
|
if (!s.tts_enabled) return;
|
||||||
try {
|
try {
|
||||||
const req = _buildTtsRequest(text, s);
|
const req = _buildTtsRequest(text, s);
|
||||||
await fetch(req.url, { method: req.method, headers: req.headers, body: req.body });
|
await _ttsViaProxy(req);
|
||||||
} catch(e) { /* silent — TTS is non-critical */ }
|
} catch(e) { /* silent — TTS is non-critical */ }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -6707,14 +6721,17 @@ async function testTTS() {
|
|||||||
if (statusEl) { statusEl.style.display = 'block'; statusEl.className = 'settings-status'; statusEl.textContent = '⏳ Invio in corso…'; }
|
if (statusEl) { statusEl.style.display = 'block'; statusEl.className = 'settings-status'; statusEl.textContent = '⏳ Invio in corso…'; }
|
||||||
try {
|
try {
|
||||||
const req = _buildTtsRequest('Test vocale Dispensa Manager', formSettings);
|
const req = _buildTtsRequest('Test vocale Dispensa Manager', formSettings);
|
||||||
const res = await fetch(req.url, { method: req.method, headers: req.headers, body: req.body });
|
const res = await _ttsViaProxy(req);
|
||||||
if (res.ok || res.status === 200) {
|
const data = await res.json().catch(() => ({}));
|
||||||
if (statusEl) { statusEl.className = 'settings-status success'; statusEl.textContent = `✅ Risposta ${res.status} — controlla che l'altoparlante abbia parlato.`; }
|
const httpCode = data.status || res.status;
|
||||||
|
if (res.ok && httpCode >= 200 && httpCode < 300) {
|
||||||
|
if (statusEl) { statusEl.className = 'settings-status success'; statusEl.textContent = `✅ Risposta ${httpCode} — controlla che l'altoparlante abbia parlato.`; }
|
||||||
} else {
|
} else {
|
||||||
if (statusEl) { statusEl.className = 'settings-status error'; statusEl.textContent = `⚠️ HTTP ${res.status}: ${res.statusText}`; }
|
const errDetail = data.error || data.body || res.statusText;
|
||||||
|
if (statusEl) { statusEl.className = 'settings-status error'; statusEl.textContent = `⚠️ HTTP ${httpCode}: ${errDetail}`; }
|
||||||
}
|
}
|
||||||
} catch(e) {
|
} catch(e) {
|
||||||
if (statusEl) { statusEl.className = 'settings-status error'; statusEl.textContent = `❌ Errore di rete: ${e.message}`; }
|
if (statusEl) { statusEl.className = 'settings-status error'; statusEl.textContent = `❌ Errore: ${e.message}`; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
Reference in New Issue
Block a user