Improve setup installation diagnostics and sync error visibility

This commit is contained in:
DevSoftTech Server 2026-03-02 23:09:40 +05:30
parent eef581a1cf
commit 9b235cc01f
5 changed files with 177 additions and 152 deletions

View File

@ -113,8 +113,6 @@ class Opd_prescription extends BaseController
'co_morbidities_prefilled_from_history' => $this->hasHistoricalCoMorbiditySelection($patientId),
'history_alerts' => $this->buildPatientHistoryAlerts((int) ($opdRow->p_id ?? 0), $opdId),
'left_remarks' => $this->fetchPatientRemarks((int) ($opdRow->p_id ?? 0), 8),
'old_rx_fast' => $this->buildOldPrescriptionFastView((int) ($opdRow->p_id ?? 0), $opdId),
'current_opd_scans' => $this->getCurrentOpdScansForConsult($opdId),
]);
}

View File

@ -79,7 +79,24 @@ class Setup extends BaseController
return $deny;
}
return view('setup/db_setup', $this->buildDbToolsViewData());
try {
return view('setup/db_setup', $this->buildDbToolsViewData());
} catch (\Throwable $e) {
log_message('error', 'Setup dbTools load failed: {message}', ['message' => $e->getMessage()]);
return view('setup/db_setup', [
'tables' => [],
'msg' => 'Setup tools could not load completely: ' . $e->getMessage(),
'lock_file' => $this->lockFilePath(),
'setup_key' => trim((string) env('setup.dbToolsKey', '')),
'sync_result' => null,
'sync_master_database' => (string) env('setup.sync.master.database', ''),
'sync_client_database' => (string) env('setup.sync.client.database', ''),
'master_schema_file' => $this->masterSchemaFilePath(),
'master_schema_exists' => is_file($this->masterSchemaFilePath()),
'diagnostics' => $this->collectSetupDiagnostics($e->getMessage()),
]);
}
}
public function schemaSync()
@ -93,19 +110,29 @@ class Setup extends BaseController
@set_time_limit(0);
@ignore_user_abort(true);
$action = strtolower(trim((string) $this->request->getPost('sync_action')));
if (!in_array($action, ['analyze', 'apply'], true)) {
$action = 'analyze';
}
try {
$action = strtolower(trim((string) $this->request->getPost('sync_action')));
if (!in_array($action, ['analyze', 'apply'], true)) {
$action = 'analyze';
}
$syncResult = $this->buildSchemaSyncPlan();
if ($action === 'apply' && empty($syncResult['errors']) && !empty($syncResult['sql'])) {
$applyResult = $this->applySchemaSyncSql($syncResult['sql']);
$syncResult['applied'] = $applyResult['applied'];
$syncResult['apply_errors'] = $applyResult['errors'];
}
$syncResult = $this->buildSchemaSyncPlan();
if ($action === 'apply' && empty($syncResult['errors']) && !empty($syncResult['sql'])) {
$applyResult = $this->applySchemaSyncSql($syncResult['sql']);
$syncResult['applied'] = $applyResult['applied'];
$syncResult['apply_errors'] = $applyResult['errors'];
}
$this->lastSyncResult = $syncResult;
$this->lastSyncResult = $syncResult;
} catch (\Throwable $e) {
log_message('error', 'Setup schemaSync failed: {message}', ['message' => $e->getMessage()]);
$this->lastSyncResult = [
'errors' => ['Schema sync failed: ' . $e->getMessage()],
'sql' => [],
'summary' => [],
'source' => 'runtime-exception',
];
}
return view('setup/db_setup', $this->buildDbToolsViewData());
}
@ -322,8 +349,18 @@ class Setup extends BaseController
*/
private function buildDbToolsViewData(): array
{
$allTables = $this->db->listTables();
$scripted = $this->detectScriptedTables();
$allTables = [];
$scripted = [];
$dbError = null;
try {
$allTables = $this->db->listTables();
$scripted = $this->detectScriptedTables();
} catch (\Throwable $e) {
$dbError = $e->getMessage();
log_message('error', 'Setup buildDbToolsViewData DB failure: {message}', ['message' => $dbError]);
session()->setFlashdata('setup_msg', 'Database connection/setup error: ' . $dbError);
}
$tableRows = [];
foreach ($allTables as $tableName) {
@ -351,6 +388,58 @@ class Setup extends BaseController
'sync_client_database' => (string) env('setup.sync.client.database', ''),
'master_schema_file' => $this->masterSchemaFilePath(),
'master_schema_exists' => is_file($this->masterSchemaFilePath()),
'diagnostics' => $this->collectSetupDiagnostics($dbError),
];
}
/**
* @return array<string, array<int, string>>
*/
private function collectSetupDiagnostics(?string $dbError = null): array
{
$errors = [];
$warnings = [];
$info = [];
$env = (string) env('CI_ENVIRONMENT', 'production');
$info[] = 'CI_ENVIRONMENT: ' . $env;
$setupKey = trim((string) env('setup.dbToolsKey', ''));
if ($setupKey === '') {
$warnings[] = 'setup.dbToolsKey is empty. Setup endpoint can only be accessed by authenticated privileged users.';
} else {
$info[] = 'setup.dbToolsKey is configured.';
}
$dbHost = trim((string) env('database.default.hostname', ''));
$dbName = trim((string) env('database.default.database', ''));
$dbUser = trim((string) env('database.default.username', ''));
if ($dbHost === '' || $dbName === '' || $dbUser === '') {
$errors[] = 'Missing database.default.* values in .env (hostname/database/username).';
} else {
$info[] = 'DB target: ' . $dbHost . ' / ' . $dbName . ' / user=' . $dbUser;
}
if ($dbError !== null && $dbError !== '') {
$errors[] = 'Database error: ' . $dbError;
}
$logPath = WRITEPATH . 'logs';
if (!is_dir($logPath) || !is_writable($logPath)) {
$warnings[] = 'writable/logs is not writable. Error diagnostics may be incomplete.';
} else {
$info[] = 'Logs path: ' . $logPath;
}
if ($this->isSetupLocked()) {
$warnings[] = 'Setup lock file exists. Remove lock file if this is a fresh installation attempt.';
}
return [
'errors' => $errors,
'warnings' => $warnings,
'info' => $info,
];
}

View File

@ -328,143 +328,12 @@
</div>
</div>
<div class="card mt-3">
<div class="card-header"><strong>Old Consult (Fast View)</strong></div>
<div class="card-header"><strong>Extracted Scan Text</strong></div>
<div class="card-body">
<div class="mb-3">
<label class="form-label mb-1"><strong>Current OPD Uploaded Scans</strong></label>
<?php if (!empty($current_opd_scans ?? [])) { ?>
<div class="row g-2">
<?php foreach (($current_opd_scans ?? []) as $scan) { ?>
<div class="col-12">
<div class="border rounded p-2">
<div class="d-flex gap-2">
<div style="width:84px;flex:0 0 84px;">
<?php if (!empty($scan['is_pdf'])) { ?>
<a class="btn btn-outline-secondary btn-sm w-100" target="_blank" href="<?= esc((string) ($scan['path'] ?? '')) ?>">PDF</a>
<?php } else { ?>
<a target="_blank" href="<?= esc((string) ($scan['path'] ?? '')) ?>">
<img src="<?= esc((string) ($scan['path'] ?? '')) ?>" loading="lazy" style="width:84px;height:72px;object-fit:cover;border:1px solid #dfe3e7;border-radius:4px;" alt="scan">
</a>
<?php } ?>
</div>
<div class="flex-grow-1 small">
<div class="mb-1">
<span class="badge bg-light text-dark border"><?= esc((string) ($scan['document_type'] ?? $scan['scan_type'] ?? 'General')) ?></span>
<?php $status = strtolower((string) ($scan['ai_status'] ?? '')); ?>
<?php if ($status === 'completed') { ?><span class="badge bg-success">AI Ready</span><?php } ?>
<?php if ($status === 'failed') { ?><span class="badge bg-danger">AI Failed</span><?php } ?>
<?php if ((int) ($scan['ai_alert_flag'] ?? 0) === 1) { ?><span class="badge bg-danger">Alert</span><?php } ?>
</div>
<?php if (!empty($scan['report_text'])) { ?>
<div class="text-muted mb-1" style="white-space:pre-line;line-height:1.25;"><?= esc((string) ($scan['report_text'] ?? '')) ?></div>
<button type="button" class="btn btn-sm btn-outline-primary btn-use-current-scan-report" data-report="<?= esc((string) ($scan['report_text'] ?? '')) ?>">Use Report</button>
<?php } else { ?>
<div class="text-muted">Report details are not available yet.</div>
<?php } ?>
<div class="text-muted mt-1"><?= esc((string) ($scan['insert_date'] ?? '')) ?></div>
</div>
</div>
</div>
</div>
<?php } ?>
</div>
<?php } else { ?>
<div class="text-muted">No uploaded scan found for this OPD yet.</div>
<?php } ?>
</div>
<?php if (!empty($old_rx_fast ?? [])) { ?>
<?php $renderedOldFastRows = 0; ?>
<?php foreach (($old_rx_fast ?? []) as $oldRow) { ?>
<?php
$oldComplaints = trim((string) ($oldRow['complaints'] ?? ''));
$oldDiagnosis = trim((string) ($oldRow['diagnosis'] ?? ''));
$oldScans = $oldRow['scans'] ?? [];
$oldMedicines = $oldRow['medicines'] ?? [];
$hasOldData = ($oldComplaints !== '' || $oldDiagnosis !== '' || !empty($oldScans) || !empty($oldMedicines));
?>
<?php if (!$hasOldData) { continue; } ?>
<?php $renderedOldFastRows++; ?>
<div class="border rounded p-2 mb-2 old-rx-card">
<div class="d-flex justify-content-between align-items-center gap-2 flex-wrap">
<div><strong><?= esc($oldRow['opd_code'] ?? '') ?></strong> <small class="text-muted"><?= esc($oldRow['opd_date'] ?? '') ?></small></div>
<?php if (!empty($oldMedicines)) { ?>
<button type="button" class="btn btn-sm btn-outline-success btn-old-rx-add-all">+ All</button>
<?php } ?>
</div>
<?php if ($oldComplaints !== '') { ?>
<div class="small"><strong>C:</strong> <?= esc($oldComplaints) ?></div>
<?php } ?>
<?php if ($oldDiagnosis !== '') { ?>
<div class="small"><strong>D:</strong> <?= esc($oldDiagnosis) ?></div>
<?php } ?>
<?php if (!empty($oldMedicines)) { ?>
<div class="table-responsive mt-2">
<table class="table table-bordered table-sm mb-1">
<thead>
<tr>
<th style="width:36px;">#</th>
<th>Medicine Name</th>
<th style="width:72px;">Action</th>
</tr>
</thead>
<tbody>
<?php foreach ($oldMedicines as $indexMed => $oldMed) { ?>
<tr>
<td><?= (int) $indexMed + 1 ?></td>
<td><?= esc((string) ($oldMed['med_name'] ?? '')) ?></td>
<td>
<button
type="button"
class="btn btn-sm btn-outline-primary btn-old-rx-add-one px-2"
title="Add medicine"
data-med-id="<?= (int) ($oldMed['med_id'] ?? 0) ?>"
data-med-name="<?= esc((string) ($oldMed['med_name'] ?? '')) ?>"
data-med-type="<?= esc((string) ($oldMed['med_type'] ?? '')) ?>"
data-dosage="<?= esc((string) ($oldMed['dosage'] ?? '')) ?>"
data-dosage-when="<?= esc((string) ($oldMed['dosage_when'] ?? '')) ?>"
data-dosage-freq="<?= esc((string) ($oldMed['dosage_freq'] ?? '')) ?>"
data-no-of-days="<?= esc((string) ($oldMed['no_of_days'] ?? '')) ?>"
data-qty="<?= esc((string) ($oldMed['qty'] ?? '')) ?>"
data-remark="<?= esc((string) ($oldMed['remark'] ?? '')) ?>"
>
+
</button>
</td>
</tr>
<?php } ?>
</tbody>
</table>
</div>
<?php } ?>
<?php if (!empty($oldScans)) { ?>
<div class="mt-2 d-flex flex-wrap gap-2">
<?php foreach ($oldScans as $scan) { ?>
<?php if (!empty($scan['is_pdf'])) { ?>
<a class="btn btn-sm btn-outline-secondary" target="_blank" href="<?= esc($scan['path']) ?>">Open PDF</a>
<?php } else { ?>
<img src="<?= esc($scan['path']) ?>" loading="lazy" style="width:70px;height:70px;object-fit:cover;border:1px solid #dfe3e7;border-radius:4px;" alt="scan">
<button type="button" class="btn btn-sm btn-outline-primary btn-scan-extract" data-file="<?= esc($scan['path']) ?>" data-type="<?= esc($scan['scan_type'] ?? 'general') ?>">Text</button>
<?php } ?>
<?php } ?>
</div>
<?php } ?>
</div>
<?php } ?>
<?php if ($renderedOldFastRows === 0) { ?>
<div class="text-muted">No old consult record found.</div>
<?php } ?>
<?php } else { ?>
<div class="text-muted">No old consult record found.</div>
<?php } ?>
<div class="mt-2">
<label class="form-label mb-1"><strong>Extracted Scan Text</strong></label>
<textarea id="scan_extracted_text" class="form-control form-control-sm" rows="5" placeholder="ECG/Lab scan text will appear here..."></textarea>
<div class="mt-1 d-flex gap-2">
<button type="button" class="btn btn-sm btn-outline-success" id="btn_scan_to_finding">Copy to Finding</button>
<button type="button" class="btn btn-sm btn-outline-success" id="btn_scan_to_investigation">Copy to Investigation</button>
</div>
<textarea id="scan_extracted_text" class="form-control form-control-sm" rows="5" placeholder="ECG/Lab scan text will appear here..."></textarea>
<div class="mt-2 d-flex gap-2">
<button type="button" class="btn btn-sm btn-outline-success" id="btn_scan_to_finding">Copy to Finding</button>
<button type="button" class="btn btn-sm btn-outline-success" id="btn_scan_to_investigation">Copy to Investigation</button>
</div>
</div>
</div>

View File

@ -12,6 +12,35 @@
<div class="alert alert-info"><?= esc($msg) ?></div>
<?php endif; ?>
<?php if (!empty($diagnostics ?? null)) : ?>
<?php if (!empty($diagnostics['errors'] ?? [])) : ?>
<div class="alert alert-danger">
<strong>Installation Diagnostics (Errors)</strong>
<?php foreach (($diagnostics['errors'] ?? []) as $diagErr) : ?>
<div><?= esc($diagErr) ?></div>
<?php endforeach; ?>
</div>
<?php endif; ?>
<?php if (!empty($diagnostics['warnings'] ?? [])) : ?>
<div class="alert alert-warning">
<strong>Installation Diagnostics (Warnings)</strong>
<?php foreach (($diagnostics['warnings'] ?? []) as $diagWarn) : ?>
<div><?= esc($diagWarn) ?></div>
<?php endforeach; ?>
</div>
<?php endif; ?>
<?php if (!empty($diagnostics['info'] ?? [])) : ?>
<div class="alert alert-success">
<strong>Installation Diagnostics (Info)</strong>
<?php foreach (($diagnostics['info'] ?? []) as $diagInfo) : ?>
<div><?= esc($diagInfo) ?></div>
<?php endforeach; ?>
</div>
<?php endif; ?>
<?php endif; ?>
<p class="text-muted">
Compares <strong>master schema file</strong> (or optional master DB) with <strong>client DB schema</strong> and generates
<code>CREATE TABLE IF NOT EXISTS</code> / <code>ALTER TABLE</code> statements.

View File

@ -37,6 +37,46 @@ if (getcwd() . DIRECTORY_SEPARATOR !== FCPATH) {
chdir(FCPATH);
}
/*
*---------------------------------------------------------------
* TEMPORARY SERVER DEBUG FALLBACK
*---------------------------------------------------------------
* New servers sometimes force CI_ENVIRONMENT=production via Apache/PHP-FPM,
* which hides the real 500 error details. Keep this block while debugging
* migration issues, then remove it after root cause is fixed.
*/
$_SERVER['CI_ENVIRONMENT'] = 'development';
putenv('CI_ENVIRONMENT=development');
error_reporting(E_ALL);
ini_set('display_errors', '1');
ini_set('display_startup_errors', '1');
ini_set('log_errors', '1');
$fallbackErrorLog = rtrim(sys_get_temp_dir(), DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . 'hms_ci4_php_errors.log';
ini_set('error_log', $fallbackErrorLog);
register_shutdown_function(static function () use ($fallbackErrorLog): void {
$lastError = error_get_last();
if ($lastError === null) {
return;
}
$fatalTypes = [E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR, E_USER_ERROR];
if (! in_array((int) ($lastError['type'] ?? 0), $fatalTypes, true)) {
return;
}
$line = sprintf(
"[%s] FATAL %s in %s:%d\n",
date('Y-m-d H:i:s'),
(string) ($lastError['message'] ?? 'Unknown fatal error'),
(string) ($lastError['file'] ?? 'unknown'),
(int) ($lastError['line'] ?? 0)
);
@file_put_contents($fallbackErrorLog, $line, FILE_APPEND);
});
/*
*---------------------------------------------------------------
* BOOTSTRAP THE APPLICATION