From 311a7bce4bf9e3510fd3970a2c6c9701ac0c4292 Mon Sep 17 00:00:00 2001 From: DevSoftTech Server Date: Mon, 30 Mar 2026 19:03:20 +0530 Subject: [PATCH] fix: mixed-content block for STT on HTTPS - add server-side proxy MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- app/Config/Routes.php | 2 + app/Controllers/Home.php | 84 ++++++++++++++++++++ app/Views/billing/opd_prescription_basic.php | 16 +++- 3 files changed, 99 insertions(+), 3 deletions(-) diff --git a/app/Config/Routes.php b/app/Config/Routes.php index 396d23b..4994e18 100644 --- a/app/Config/Routes.php +++ b/app/Config/Routes.php @@ -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'); diff --git a/app/Controllers/Home.php b/app/Controllers/Home.php index 15f4870..95e2d98 100644 --- a/app/Controllers/Home.php +++ b/app/Controllers/Home.php @@ -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(); diff --git a/app/Views/billing/opd_prescription_basic.php b/app/Views/billing/opd_prescription_basic.php index 48fdd00..2532835 100644 --- a/app/Views/billing/opd_prescription_basic.php +++ b/app/Views/billing/opd_prescription_basic.php @@ -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 ? '' : 'http://139.59.13.39:8000/health'; + var complaintsSttServerTranscribeUrl = _onHttps ? '' : '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() {