fix: mixed-content block for STT on HTTPS - add server-side proxy

Production runs HTTPS; browser blocks http://139.59.13.39:8000 (mixed content).
Solution: CI4 proxy endpoints forward audio/health check server-side (HTTP→HTTP).

- Home::sttProxy()       POST stt-proxy    - forwards audio to STT server
- Home::sttProxyHealth() GET  stt-proxy/health - pings STT and returns {ok:bool}
- JS: auto-detect window.location.protocol; use proxy URLs on HTTPS,
  direct IP URLs on HTTP (local/dev)
- checkComplaintsSttServer: parse {ok:bool} from proxy health response
This commit is contained in:
DevSoftTech Server 2026-03-30 19:03:20 +05:30
parent e6c9939b09
commit 311a7bce4b
3 changed files with 99 additions and 3 deletions

View File

@ -10,6 +10,8 @@ $routes->get('dashboard', 'Home::dashboard');
$routes->get('my-profile', 'Home::myProfile');
$routes->post('my-profile/save', 'Home::myProfileSave');
$routes->get('speech-test', 'Home::speechTest');
$routes->post('stt-proxy', 'Home::sttProxy');
$routes->get('stt-proxy/health', 'Home::sttProxyHealth');
// Pharmacy (Medical) - phase 1
$routes->get('Medical', 'Medical::index');

View File

@ -246,6 +246,90 @@ class Home extends BaseController
return view('tools/speech_test');
}
/**
* Server-side proxy for the STT service.
* Forwards audio from HTTPS clients to the HTTP STT server,
* avoiding browser mixed-content blocks.
*/
public function sttProxy()
{
$authRedirect = $this->ensureAuthenticated();
if ($authRedirect !== null) {
return $this->response->setStatusCode(401)->setJSON(['error' => 'Unauthorized']);
}
$file = $this->request->getFile('audio');
if ($file === null || ! $file->isValid()) {
return $this->response->setStatusCode(400)->setJSON(['error' => 'No valid audio file received']);
}
$lang = (string) ($this->request->getPost('lang') ?? 'en-IN');
$context = (string) ($this->request->getPost('medical_context') ?? '');
$sttUrl = 'http://139.59.13.39:8000/stt/transcribe';
$tmpPath = $file->getTempName();
$mime = $file->getMimeType() ?: 'audio/webm';
$name = $file->getName() ?: 'audio.webm';
$ch = curl_init($sttUrl);
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 30,
CURLOPT_POSTFIELDS => [
'audio' => new \CURLFile($tmpPath, $mime, $name),
'lang' => $lang,
'medical_context' => $context,
],
]);
$body = curl_exec($ch);
$httpCode = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE);
$curlErr = curl_error($ch);
curl_close($ch);
if ($body === false || $curlErr !== '') {
return $this->response->setStatusCode(502)
->setJSON(['error' => 'STT server unreachable', 'detail' => $curlErr]);
}
$decoded = json_decode($body, true);
if ($decoded === null) {
return $this->response->setStatusCode(502)
->setJSON(['error' => 'Invalid response from STT server']);
}
return $this->response->setStatusCode($httpCode > 0 ? $httpCode : 200)
->setContentType('application/json')
->setBody($body);
}
/**
* Proxy health check — pings the STT server and returns its status.
* Lets the browser check availability over HTTPS without mixed-content issues.
*/
public function sttProxyHealth()
{
$authRedirect = $this->ensureAuthenticated();
if ($authRedirect !== null) {
return $this->response->setStatusCode(401)->setJSON(['ok' => false]);
}
$ch = curl_init('http://139.59.13.39:8000/health');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 4,
CURLOPT_NOBODY => false,
]);
$body = curl_exec($ch);
$httpCode = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
$ok = ($body !== false && $httpCode >= 200 && $httpCode < 300);
return $this->response->setStatusCode(200)->setJSON(['ok' => $ok]);
}
public function myProfileSave()
{
$authRedirect = $this->ensureAuthenticated();

View File

@ -959,8 +959,10 @@
var medicalSttStream = null;
var medicalSttChunks = [];
var medicalSttActive = false;
var complaintsSttServerHealthUrl = 'http://139.59.13.39:8000/health';
var complaintsSttServerTranscribeUrl = 'http://139.59.13.39:8000/stt/transcribe';
// Use server-side proxy when page is HTTPS (avoids mixed-content block)
var _onHttps = (window.location.protocol === 'https:');
var complaintsSttServerHealthUrl = _onHttps ? '<?= base_url('stt-proxy/health') ?>' : 'http://139.59.13.39:8000/health';
var complaintsSttServerTranscribeUrl = _onHttps ? '<?= base_url('stt-proxy') ?>' : 'http://139.59.13.39:8000/stt/transcribe';
var draftKey = 'opd_rx_draft_' + ($('#opd_id').val() || '0');
var templateLoadState = { target: '', section: '', rows: [] };
var templateSaveState = { section: '', text: '', scope: 'doctor' };
@ -1244,7 +1246,15 @@
mode: 'cors',
signal: controller.signal
}).then(function(response) {
return !!(response && response.ok);
if (!response || !response.ok) {
return false;
}
// Proxy returns {ok: bool}; direct server returns 200 with any body
return response.json().then(function(data) {
return data && data.ok !== false;
}).catch(function() {
return true; // direct server: 200 with non-JSON body is fine
});
}).catch(function() {
return false;
}).finally(function() {