Improve setup installation diagnostics and sync error visibility
This commit is contained in:
parent
eef581a1cf
commit
9b235cc01f
|
|
@ -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),
|
||||
]);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
];
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user