docs(install): add Ubuntu deployment guide, automated installer script, and starter database

This commit is contained in:
DevSoftTech Server 2026-10-27 21:45:17 +05:30
parent 61a3503fde
commit 5be2644032
5 changed files with 93650 additions and 0 deletions

360
app/Commands/HmsInstall.php Normal file
View File

@ -0,0 +1,360 @@
<?php
namespace App\Commands;
use CodeIgniter\CLI\BaseCommand;
use CodeIgniter\CLI\CLI;
use CodeIgniter\Shield\Entities\User;
use CodeIgniter\Shield\Models\UserModel;
class HmsInstall extends BaseCommand
{
protected $group = 'HMS';
protected $name = 'hms:install';
protected $description = 'Initialize and setup HMS: schema, master tables, templates, admin user & hospital profile.';
protected $usage = 'hms:install [options]';
protected $arguments = [];
protected $options = [
'--hospital-name' => 'Name of the hospital to configure in hospital_setting (default: "Johri Hospital")',
'--admin-user' => 'Username for superadmin account (default: "admin")',
'--admin-email' => 'Email for superadmin account (default: "admin@example.com")',
'--admin-password' => 'Password for superadmin account (default: "Admin@12345")',
'--force' => 'Force import even if tables already exist in the database',
'--skip-db-import' => 'Skip importing schema/masters and only configure admin user and hospital name',
];
public function run(array $params): void
{
CLI::write("==================================================", 'cyan');
CLI::write(" HMS Installation & Setup Tool ", 'cyan');
CLI::write("==================================================", 'cyan');
$hospitalName = CLI::getOption('hospital-name') ?? 'Johri Hospital';
$adminUser = CLI::getOption('admin-user') ?? 'admin';
$adminEmail = CLI::getOption('admin-email') ?? 'admin@example.com';
$adminPassword = CLI::getOption('admin-password') ?? 'Admin@12345';
$force = CLI::getOption('force') !== null;
$skipDbImport = CLI::getOption('skip-db-import') !== null;
// 1. Ensure writable directories
$this->ensureWritableDirectories();
// 2. Connect to database
try {
$db = \Config\Database::connect();
$dbName = $db->getDatabase();
CLI::write("Connected to database: {$dbName}", 'green');
} catch (\Throwable $e) {
CLI::error("Database connection failed: " . $e->getMessage());
return;
}
// 3. Check table count
$tables = $db->listTables();
$tableCount = count($tables);
CLI::write("Current table count in '{$dbName}': {$tableCount}", 'yellow');
// 4. Import Starter Schema & Masters if needed
if (!$skipDbImport && ($tableCount === 0 || $force)) {
CLI::write("Starting database initialization...", 'yellow');
$importOk = $this->importStarterSql($db);
if (!$importOk) {
CLI::error("Failed to import starter database SQL. Aborting.");
return;
}
$tables = $db->listTables();
CLI::write("Database successfully populated with " . count($tables) . " tables.", 'green');
} elseif ($tableCount > 0 && !$force) {
CLI::write("Database already contains {$tableCount} tables. Skipping SQL import (use --force to overwrite).", 'yellow');
}
// 5. Ensure Admin User
CLI::write("Configuring Admin User ({$adminUser})...", 'yellow');
$this->configureAdminUser($db, $adminUser, $adminEmail, $adminPassword, $hospitalName);
// 6. Configure Hospital Profile
CLI::write("Setting Hospital Name: '{$hospitalName}'...", 'yellow');
$this->configureHospitalSetting($db, 'H_Name', $hospitalName);
// 7. Success Banner
CLI::write("\n==================================================", 'green');
CLI::write(" HMS Installation Completed! ", 'green');
CLI::write("==================================================", 'green');
CLI::write(" Hospital Name : {$hospitalName}");
CLI::write(" Database : {$dbName}");
CLI::write(" Admin User : {$adminUser}");
CLI::write(" Admin Email : {$adminEmail}");
CLI::write(" Admin Password: {$adminPassword}");
CLI::write("--------------------------------------------------");
CLI::write("You can now login at: " . site_url('login'), 'cyan');
CLI::write("==================================================\n", 'green');
}
private function ensureWritableDirectories(): void
{
$dirs = [
WRITEPATH . 'cache',
WRITEPATH . 'logs',
WRITEPATH . 'session',
WRITEPATH . 'uploads',
WRITEPATH . 'debugbar',
FCPATH . 'assets/images',
];
foreach ($dirs as $dir) {
if (!is_dir($dir)) {
@mkdir($dir, 0775, true);
}
if (is_dir($dir) && !is_writable($dir)) {
@chmod($dir, 0775);
}
}
CLI::write("Writable directories checked and ready.", 'green');
}
private function importStarterSql($db): bool
{
$sqlPath = ROOTPATH . 'database/hms_starter_schema_and_masters.sql';
$gzPath = ROOTPATH . 'database/hms_starter_schema_and_masters.sql.gz';
if (!is_file($sqlPath) && is_file($gzPath)) {
CLI::write("Decompressing {$gzPath}...", 'yellow');
$sqlPath = $this->decompressGz($gzPath);
}
if (!is_file($sqlPath)) {
CLI::error("Starter SQL file not found at: {$sqlPath}");
return false;
}
$host = (string) $db->hostname;
$port = (string) ($db->port ?? 3306);
$user = (string) $db->username;
$pass = (string) $db->password;
$name = (string) $db->database;
// Try using mysql command-line tool first (fastest and zero-memory)
$mysqlCmd = "mysql -h " . escapeshellarg($host) . " -P " . escapeshellarg($port) . " -u " . escapeshellarg($user);
if ($pass !== '') {
$mysqlCmd .= " -p" . escapeshellarg($pass);
}
$mysqlCmd .= " " . escapeshellarg($name) . " < " . escapeshellarg($sqlPath) . " 2>&1";
exec($mysqlCmd, $out, $ret);
if ($ret === 0) {
CLI::write("SQL imported successfully via mysql command line.", 'green');
return true;
}
// Fallback: Read in blocks and execute using mysqli
CLI::write("CLI import exited with code {$ret}. Falling back to PHP mysqli execution...", 'yellow');
return $this->importSqlViaPhp($db, $sqlPath);
}
private function decompressGz(string $gzPath): string
{
$outPath = sys_get_temp_dir() . '/hms_starter_schema_and_masters.sql';
$gz = gzopen($gzPath, 'rb');
$out = fopen($outPath, 'wb');
while (!gzeof($gz)) {
fwrite($out, gzread($gz, 1024 * 512));
}
gzclose($gz);
fclose($out);
return $outPath;
}
private function importSqlViaPhp($db, string $sqlPath): bool
{
$fp = fopen($sqlPath, 'r');
if (!$fp) {
CLI::error("Unable to open SQL file: {$sqlPath}");
return false;
}
$db->query('SET FOREIGN_KEY_CHECKS = 0');
$db->query('SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO"');
$buffer = '';
$count = 0;
while (!feof($fp)) {
$line = fgets($fp);
if ($line === false) {
break;
}
$trimmed = trim($line);
if ($trimmed === '' || str_starts_with($trimmed, '--') || str_starts_with($trimmed, '/*')) {
continue;
}
$buffer .= $line;
if (str_ends_with($trimmed, ';')) {
try {
$db->query($buffer);
$count++;
} catch (\Throwable $e) {
// Ignore duplicate key or exists errors
}
$buffer = '';
}
}
fclose($fp);
$db->query('SET FOREIGN_KEY_CHECKS = 1');
CLI::write("PHP import completed ({$count} statements executed).", 'green');
return true;
}
private function configureAdminUser($db, string $username, string $email, string $password, string $hospitalName = ''): void
{
$userId = null;
try {
$userModel = model(UserModel::class);
$user = $userModel->where('username', $username)->first();
if ($user === null) {
$user = new User([
'username' => $username,
'email' => $email,
'password' => $password,
'active' => 1,
]);
$userModel->save($user);
$userId = $userModel->getInsertID();
$user = $userModel->findById($userId);
CLI::write("Created new admin user: {$username} (ID: {$userId})", 'green');
} else {
$userId = (int) $user->id;
$user->email = $email;
$user->setPassword($password);
$user->active = 1;
$userModel->save($user);
CLI::write("Updated existing admin user: {$username}", 'green');
}
if ($user && method_exists($user, 'addGroup')) {
$user->addGroup('admin');
$user->addGroup('superadmin');
}
} catch (\Throwable $e) {
// Direct database fallback if Shield models fail
CLI::write("Shield model notice: " . $e->getMessage() . ". Using direct auth table upsert.", 'yellow');
$userId = $this->directAdminUserUpsert($db, $username, $email, $password);
}
// Set Admin Profile Name based on Hospital Name
if ($userId !== null && $userId > 0) {
$this->applyAdminProfileName($db, $userId, $hospitalName);
}
}
private function directAdminUserUpsert($db, string $username, string $email, string $password): int
{
$now = date('Y-m-d H:i:s');
$userRow = $db->table('users')->where('username', $username)->get()->getRowArray();
if ($userRow) {
$userId = (int) $userRow['id'];
$db->table('users')->where('id', $userId)->update([
'active' => 1,
'updated_at' => $now,
]);
} else {
$db->table('users')->insert([
'username' => $username,
'active' => 1,
'created_at' => $now,
'updated_at' => $now,
]);
$userId = (int) $db->insertID();
}
$hash = password_hash($password, PASSWORD_BCRYPT, ['cost' => 12]);
$identity = $db->table('auth_identities')
->where('user_id', $userId)
->where('type', 'email_password')
->get()->getRowArray();
if ($identity) {
$db->table('auth_identities')->where('id', $identity['id'])->update([
'secret' => $email,
'secret2' => $hash,
'updated_at' => $now,
]);
} else {
$db->table('auth_identities')->insert([
'user_id' => $userId,
'type' => 'email_password',
'secret' => $email,
'secret2' => $hash,
'created_at' => $now,
'updated_at' => $now,
]);
}
// Add to admin group
$hasGroup = $db->table('auth_groups_users')
->where('user_id', $userId)
->where('group', 'admin')
->countAllResults();
if ($hasGroup === 0) {
$db->table('auth_groups_users')->insert([
'user_id' => $userId,
'group' => 'admin',
'created_at' => $now,
]);
}
CLI::write("Admin user configured in auth tables successfully.", 'green');
return $userId;
}
private function applyAdminProfileName($db, int $userId, string $hospitalName): void
{
$adminFullName = trim($hospitalName) !== '' ? trim($hospitalName) . ' Admin' : 'Hospital Administrator';
$now = date('Y-m-d H:i:s');
// 1. Update auth_identities.extra
$extraPayload = json_encode([
'full_name' => $adminFullName,
'phone_no' => '',
]);
$db->table('auth_identities')
->where('user_id', $userId)
->where('type', 'email_password')
->update(['extra' => $extraPayload]);
// 2. Update user_profiles if table exists
if ($db->tableExists('user_profiles')) {
$existing = $db->table('user_profiles')->where('user_id', $userId)->get(1)->getRowArray();
if ($existing) {
$db->table('user_profiles')->where('user_id', $userId)->update([
'full_name' => $adminFullName,
'phone_no' => '',
'updated_at' => $now,
]);
} else {
$db->table('user_profiles')->insert([
'user_id' => $userId,
'full_name' => $adminFullName,
'phone_no' => '',
'created_at' => $now,
'updated_at' => $now,
]);
}
}
CLI::write("Admin display name set to: '{$adminFullName}'", 'green');
}
private function configureHospitalSetting($db, string $key, string $value): void
{
$existing = $db->table('hospital_setting')->where('s_name', $key)->countAllResults();
if ($existing > 0) {
$db->table('hospital_setting')->where('s_name', $key)->update(['s_value' => $value]);
} else {
$db->table('hospital_setting')->insert(['s_name' => $key, 's_value' => $value]);
}
}
}

File diff suppressed because one or more lines are too long

Binary file not shown.

View File

@ -0,0 +1,328 @@
# E-Atria HMS (Hospital Management System) — Ubuntu Installation Guide
This guide provides instructions for deploying and installing **E-Atria HMS (CodeIgniter 4)** on an **Ubuntu Linux server** (Ubuntu 20.04, 22.04, or 24.04 LTS).
---
## Table of Contents
1. [System Prerequisites](#1-system-prerequisites)
2. [Method A: Automated 1-Command Installation (Recommended)](#2-method-a-automated-1-command-installation-recommended)
3. [Method B: Manual Step-by-Step Installation](#3-method-b-manual-step-by-step-installation)
4. [Database Architecture & Starter Data](#4-database-architecture--starter-data)
5. [Configuring Apache & SSL](#5-configuring-apache--ssl)
6. [Post-Installation Verification & First Login](#6-post-installation-verification--first-login)
7. [Recommended PHP & Server Settings](#7-recommended-php--server-settings)
8. [Automated Background Cron Jobs](#8-automated-background-cron-jobs)
9. [Troubleshooting Common Issues](#9-troubleshooting-common-issues)
---
## 1. System Prerequisites
| Component | Recommended Version | Details |
| :--- | :--- | :--- |
| **Operating System** | Ubuntu 22.04 / 24.04 LTS | Standard server image |
| **Web Server** | Apache 2.4+ | Modules: `rewrite`, `ssl`, `headers` |
| **PHP** | PHP 8.2 or 8.3 | Required extensions listed below |
| **Database** | MySQL 8.0+ or MariaDB 10.5+ | UTF8MB4 charset support |
| **Package Managers** | Git & Composer 2+ | For code updates and dependencies |
### Required PHP Extensions:
- `php8.3-cli`, `php8.3-fpm` (or `libapache2-mod-php8.3`)
- `php8.3-mysql`, `php8.3-curl`, `php8.3-gd`, `php8.3-mbstring`, `php8.3-xml`, `php8.3-intl`, `php8.3-zip`, `php8.3-bcmath`
---
## 2. Method A: Automated 1-Command Installation (Recommended)
The repository provides an automated installation script in [`scripts/install_ubuntu.sh`](file:///d:/Workplace/HMS_CI4_OLD/scripts/install_ubuntu.sh).
### Syntax:
```bash
sudo bash scripts/install_ubuntu.sh <domain_name> <database_name> "<hospital_name>"
```
### Real-World Example:
```bash
sudo bash scripts/install_ubuntu.sh johrihospital.e-atria.net hms_johri_ss_hospital "Johri Hospital"
```
### Interactive Mode:
If you run without arguments, the script will interactively prompt for:
- Domain Name (e.g. `johrihospital.e-atria.net`)
- Database Name (e.g. `hms_johri_ss_hospital`)
- Hospital Name (e.g. `Johri Hospital`)
- MySQL Root Password
The script will automatically:
1. Update system packages and install Apache, PHP 8.3, MySQL client, Composer, and Git.
2. Enable required Apache modules (`rewrite`, `ssl`, `headers`).
3. Clone or pull the repository to `/var/www/html/eatria_client/<slug>` or `/var/www/html/<slug>`.
4. Create the MySQL database if it does not already exist.
5. Create `.env` configured with your database and domain.
6. Populate all 399 tables with the clean master schema, templates, and clinical datasets.
7. Setup the Superadmin user account (`admin` / `Admin@12345`).
8. Generate and activate the Apache VirtualHost with SSL.
9. Test and verify HTTP 200 response on `/login`.
---
## 3. Method B: Manual Step-by-Step Installation
If you prefer full manual control, follow these steps:
### Step 1: Install System Packages
```bash
sudo apt-get update
sudo apt-get install -y apache2 mysql-server git unzip curl ca-certificates
# Install PHP 8.3 and extensions
sudo apt-get install -y \
php8.3 php8.3-cli php8.3-fpm php8.3-mysql php8.3-curl \
php8.3-gd php8.3-mbstring php8.3-xml php8.3-intl php8.3-zip php8.3-bcmath
# Enable Apache modules
sudo a2enmod rewrite ssl headers proxy proxy_fcgi
sudo systemctl restart apache2
```
### Step 2: Clone the Application
```bash
sudo mkdir -p /var/www/html/eatria_client/johrihospital
sudo git clone https://git.e-atria.net/d_s_bisht/devsofttech_hms_ci4.git /var/www/html/eatria_client/johrihospital
cd /var/www/html/eatria_client/johrihospital
# Install Composer dependencies
composer install --no-dev --optimize-autoloader
```
### Step 3: Configure Writable Directory Permissions
CodeIgniter 4 requires write permissions on several cache and storage directories:
```bash
cd /var/www/html/eatria_client/johrihospital
# Ensure all subdirectories exist
sudo mkdir -p \
writable/cache \
writable/logs \
writable/session \
writable/uploads \
writable/debugbar \
public/assets/images
# Assign ownership to web server
sudo chown -R www-data:www-data /var/www/html/eatria_client/johrihospital
sudo chmod -R 775 writable public/assets/images
```
### Step 4: Configure Environment (`.env`)
Create `.env` in the application root directory:
```bash
cat << 'EOF' > .env
CI_ENVIRONMENT = production
app.baseURL = 'https://johrihospital.e-atria.net/'
database.default.hostname = localhost
database.default.username = root
database.default.password = YourStrongPassword
database.default.database = hms_johri_ss_hospital
database.default.DBDriver = MySQLi
database.default.port = 3306
database.default.DBDebug = false
setup.dbToolsKey = yourStrongSetupKey123
snomed.csnotk.baseUrl = https://csnotk.e-atria.in/csnoserv
snomed.csnotk.enabled = true
snomed.csnotk.timeoutSec = 10
ABDM_BRIDGE_PUBLIC_URL=https://abdm-bridge.e-atria.in
abdm.connector = dreamsoft
abdm.directBaseUrl = https://dev.abdm.gov.in/api/v3
abdm.directTimeoutSec = 30
BRIDGE_SYNC_URL = https://csnotk.e-atria.in/api/bridge
ABDM_BRIDGE_URL = https://abdm-bridge.e-atria.in/api/v1/bridge
BRIDGE_SYNC_TIMEOUT = 20
BRIDGE_SYNC_PROVIDER = bridge
EOF
sudo chmod 640 .env
sudo chown www-data:www-data .env
```
### Step 5: Initialize Database & Run Installer
Create the database in MySQL:
```bash
mysql -u root -p -e "CREATE DATABASE IF NOT EXISTS \`hms_johri_ss_hospital\` CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;"
```
Run the built-in Spark installation command:
```bash
php spark hms:install \
--hospital-name="Johri Hospital" \
--admin-user="admin" \
--admin-email="admin@example.com" \
--admin-password="Admin@12345"
```
*Alternatively*, import the starter SQL file directly via MySQL CLI:
```bash
# Uncompressed
mysql -u root -p hms_johri_ss_hospital < database/hms_starter_schema_and_masters.sql
# Gzipped
zcat database/hms_starter_schema_and_masters.sql.gz | mysql -u root -p hms_johri_ss_hospital
```
---
## 4. Database Architecture & Starter Data
The starter database ([`database/hms_starter_schema_and_masters.sql`](file:///d:/Workplace/HMS_CI4_OLD/database/hms_starter_schema_and_masters.sql)):
- **399 Total Tables**: Complete HMS schema with all relations, primary keys, and indexes.
- **Clean State**: Transactional tables start with **0 rows**:
- `patient_master`, `opd_master`, `opd_prescription` = 0 rows
- `ipd_master`, `ipd_discharge`, `invoice_master`, `payment_history` = 0 rows
- `sales_master`, `purchase_invoice`, `lab_request`, `abdm_api_logs` = 0 rows
- **Pre-populated Master Tables & Templates** (186 master tables with 83,000+ records):
- **Clinical Templates**: `opd_clinical_templates`, `ipd_discharge_templates`, `doc_format_master`, `doc_print_templates`, `diagnosis_print_templates`
- **Drug Timings & Frequencies**: `opd_dose_frequency`, `opd_dose_when`, `opd_dose_where`, `opd_dose_duration`
- **Investigation & Lab Masters**: `investigation`, `invprofiles`, `lab_tests`, `lab_rgroups`
- **Ayushman Packages**: `ayushman_package_master` (1,401 standardized PM-JAY packages)
- **Departments & Charge Items**: `hc_department`, `hc_items`, `ipd_item_type`
- **Reference Lists**: `blood_group`, `color`, `india_state`, `name_list`, `med_company`, `med_formulation`
- **Shield Auth & Superadmin**: `users`, `auth_identities`, `auth_groups_users`
- **Database Migrations Record**: `migrations` (all 132 migrations recorded as up-to-date)
---
## 5. Configuring Apache & SSL
Create `/etc/apache2/sites-available/johrihospital.conf`:
```apache
<VirtualHost *:80>
ServerName johrihospital.e-atria.net
Redirect permanent / https://johrihospital.e-atria.net/
</VirtualHost>
<VirtualHost *:443>
ServerName johrihospital.e-atria.net
SSLEngine on
SSLCertificateFile /etc/letsencrypt/live/e-atria.net/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/e-atria.net/privkey.pem
DocumentRoot /var/www/html/eatria_client/johrihospital/public
<Directory /var/www/html/eatria_client/johrihospital/public>
Options +FollowSymLinks
AllowOverride All
Require all granted
DirectoryIndex index.php index.html index.htm
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php/$1 [L]
</IfModule>
</Directory>
ErrorLog /var/log/apache2/johrihospital_error.log
CustomLog /var/log/apache2/johrihospital_access.log combined
</VirtualHost>
```
### Enable Site and Reload Apache:
```bash
sudo a2ensite johrihospital.conf
sudo apache2ctl configtest
sudo systemctl reload apache2
```
---
## 6. Post-Installation Verification & First Login
1. Open your browser and navigate to:
```
https://johrihospital.e-atria.net/login
```
2. Log in using default superadmin credentials:
- **Username / Email:** `admin` (or `devender.singh.bisht@gmail.com`)
- **Password:** `Admin@12345`
3. Enter the 4-digit Security CAPTCHA code shown on the screen and click **Login**.
4. Once logged in:
- Navigate to **Setting -> Hospital Profile** (`/setting/admin/hospital-profile`) to customize Hospital Name, Address, Contact details, Hospital Logo, and Login Wallpaper.
---
## 7. Recommended PHP & Server Settings
In `/etc/php/8.3/apache2/php.ini` (or `/etc/php/8.3/fpm/php.ini`):
```ini
memory_limit = 512M
upload_max_filesize = 50M
post_max_size = 50M
max_execution_time = 300
max_input_vars = 5000
date.timezone = Asia/Kolkata
```
Reload web server after changes:
```bash
sudo systemctl restart apache2
# or sudo systemctl restart php8.3-fpm
```
---
## 8. Automated Background Cron Jobs
To ensure seamless ABDM synchronization, SMS notifications, and background log pruning, add the following to `crontab -e`:
```cron
# Process ABDM Work Task Board Care Context Linking every 2 minutes
*/2 * * * * cd /var/www/html/eatria_client/johrihospital && php spark abdm:taskboard-sync > /dev/null 2>&1
# Push stored health records to ABDM Bridge every 5 minutes
*/5 * * * * cd /var/www/html/eatria_client/johrihospital && php spark abdm:push-sync > /dev/null 2>&1
# Prune old ABDM logs daily at 02:00 AM
0 2 * * * cd /var/www/html/eatria_client/johrihospital && php spark abdm:prune-logs > /dev/null 2>&1
```
---
## 9. Troubleshooting Common Issues
### Issue 1: HTTP 500 - `CacheException::forUnableToWrite()`
- **Cause:** Missing `writable/cache` directory.
- **Fix:**
```bash
sudo mkdir -p /var/www/html/eatria_client/johrihospital/writable/cache
sudo chown -R www-data:www-data /var/www/html/eatria_client/johrihospital/writable
sudo chmod -R 775 /var/www/html/eatria_client/johrihospital/writable
```
### Issue 2: 404 Not Found on Inner Routes (e.g. `/login`, `/dashboard`)
- **Cause:** Apache `mod_rewrite` is disabled, or `.htaccess` is ignored because `AllowOverride None` is set.
- **Fix:**
```bash
sudo a2enmod rewrite
# Ensure "AllowOverride All" is set in <Directory .../public> in the virtual host config
sudo systemctl restart apache2
```
### Issue 3: MySQL "Access Denied" or Strict Mode Error
- **Cause:** Incorrect credentials in `.env` or MySQL strict group-by mode.
- **Fix:** Verify database settings in `.env` and test connection:
```bash
cd /var/www/html/eatria_client/johrihospital
php spark db:table hospital_setting
```

344
scripts/install_ubuntu.sh Normal file
View File

@ -0,0 +1,344 @@
#!/usr/bin/env bash
# ==============================================================================
# E-Atria HMS (Hospital Management System) - Ubuntu Auto-Installer
# ==============================================================================
# This script automates the complete installation of HMS CI4 on Ubuntu:
# 1. System packages (PHP 8.3, Apache2, MySQL/MariaDB, Composer, Git)
# 2. Apache modules (rewrite, ssl, headers)
# 3. Target application cloning/copying & permission setup
# 4. Environment (.env) configuration
# 5. Database initialization (Schema + Masters + Templates via Starter SQL)
# 6. Admin user & Hospital setting configuration via 'php spark hms:install'
# 7. Apache VirtualHost & SSL configuration
# ==============================================================================
set -euo pipefail
# Text colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
CYAN='\033[0;36m'
BOLD='\033[1m'
NC='\033[0m' # No Color
echo -e "${CYAN}${BOLD}"
echo "======================================================================"
echo " E-Atria Hospital Management System (HMS CI4) "
echo " Ubuntu Installation Script "
echo "======================================================================"
echo -e "${NC}"
# Check root/sudo
if [ "$(id -u)" -ne 0 ]; then
echo -e "${RED}[ERROR] This script must be run as root or with sudo.${NC}"
exit 1
fi
# Detect OS
if [ -f /etc/os-release ]; then
. /etc/os-release
OS_NAME=$NAME
OS_VER=$VERSION_ID
else
OS_NAME="Linux"
OS_VER="Unknown"
fi
echo -e "${GREEN}[INFO] Detected OS: ${OS_NAME} (${OS_VER})${NC}"
# Configuration Variables (Defaults can be overridden via environment or interactive prompts)
DOMAIN="${1:-${DOMAIN:-}}"
DB_NAME="${2:-${DB_NAME:-}}"
HOSPITAL_NAME="${3:-${HOSPITAL_NAME:-}}"
DB_USER="${DB_USER:-root}"
DB_PASS="${DB_PASS:-}"
DB_HOST="${DB_HOST:-localhost}"
DB_PORT="${DB_PORT:-3306}"
ADMIN_USER="${ADMIN_USER:-admin}"
ADMIN_EMAIL="${ADMIN_EMAIL:-admin@example.com}"
ADMIN_PASS="${ADMIN_PASS:-Admin@12345}"
REPO_URL="${REPO_URL:-https://git.e-atria.net/d_s_bisht/devsofttech_hms_ci4.git}"
SSL_CERT="${SSL_CERT:-/etc/letsencrypt/live/e-atria.net/fullchain.pem}"
SSL_KEY="${SSL_KEY:-/etc/letsencrypt/live/e-atria.net/privkey.pem}"
# Interactive prompts if arguments not provided
if [ -z "$DOMAIN" ]; then
read -rp "Enter Domain Name (e.g. johrihospital.e-atria.net): " DOMAIN
fi
if [ -z "$DB_NAME" ]; then
read -rp "Enter Database Name (e.g. hms_johri_ss_hospital): " DB_NAME
fi
if [ -z "$HOSPITAL_NAME" ]; then
read -rp "Enter Hospital Name (e.g. Johri Hospital): " HOSPITAL_NAME
fi
if [ -z "$DB_PASS" ]; then
read -rsp "Enter MySQL Password for user '$DB_USER': " DB_PASS
echo ""
fi
INSTALL_DIR="/var/www/html/eatria_client/${DOMAIN%%.*}"
if [ -d "/var/www/html/${DOMAIN%%.*}" ]; then
INSTALL_DIR="/var/www/html/${DOMAIN%%.*}"
fi
echo -e "\n${YELLOW}${BOLD}Configuration Summary:${NC}"
echo " Domain Name : $DOMAIN"
echo " Target Dir : $INSTALL_DIR"
echo " Database Name : $DB_NAME"
echo " Database User : $DB_USER"
echo " Hospital Name : $HOSPITAL_NAME"
echo " Admin User : $ADMIN_USER"
echo " Admin Password : [HIDDEN]"
echo ""
# ------------------------------------------------------------------------------
# Step 1: Install System Prerequisites
# ------------------------------------------------------------------------------
echo -e "${CYAN}--> Step 1: Checking and installing system packages...${NC}"
export DEBIAN_FRONTEND=noninteractive
apt-get update -y -q
# Install software-properties-common if missing
apt-get install -y -q software-properties-common curl git unzip ufw ca-certificates
# Add ondrej/php PPA if PHP 8.3 not available
if ! command -v php8.3 &>/dev/null; then
echo -e "${YELLOW}Adding ondrej/php repository for PHP 8.3...${NC}"
add-apt-repository -y ppa:ondrej/php
apt-get update -y -q
fi
# Install Apache, MySQL client, PHP 8.3 and required extensions
apt-get install -y -q \
apache2 \
mysql-client \
php8.3 \
php8.3-cli \
php8.3-fpm \
php8.3-mysql \
php8.3-curl \
php8.3-gd \
php8.3-mbstring \
php8.3-xml \
php8.3-intl \
php8.3-zip \
php8.3-bcmath
# Enable Apache modules
echo -e "${CYAN}--> Enabling required Apache modules...${NC}"
a2enmod rewrite ssl headers proxy proxy_fcgi setenvif -q
# Check Composer
if ! command -v composer &>/dev/null; then
echo -e "${YELLOW}Installing Composer...${NC}"
curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
fi
# ------------------------------------------------------------------------------
# Step 2: Clone or Copy Application Files
# ------------------------------------------------------------------------------
echo -e "${CYAN}--> Step 2: Setting up application in ${INSTALL_DIR}...${NC}"
mkdir -p "$(dirname "$INSTALL_DIR")"
if [ ! -d "$INSTALL_DIR/.git" ]; then
echo -e "${YELLOW}Cloning repository from ${REPO_URL}...${NC}"
git clone "$REPO_URL" "$INSTALL_DIR"
else
echo -e "${GREEN}Repository already cloned. Pulling latest changes...${NC}"
cd "$INSTALL_DIR"
git pull origin main || true
fi
cd "$INSTALL_DIR"
# Install/Copy vendor dependencies
if [ ! -d "$INSTALL_DIR/vendor" ]; then
if [ -d "/var/www/html/hms_etria/vendor" ]; then
echo -e "${YELLOW}Copying pre-installed vendor libraries from hms_etria...${NC}"
cp -a /var/www/html/hms_etria/vendor "$INSTALL_DIR/vendor"
else
echo -e "${YELLOW}Running composer install...${NC}"
composer install --no-dev --optimize-autoloader
fi
fi
# Ensure all writable subdirectories exist
mkdir -p \
"$INSTALL_DIR/writable/cache" \
"$INSTALL_DIR/writable/logs" \
"$INSTALL_DIR/writable/session" \
"$INSTALL_DIR/writable/uploads" \
"$INSTALL_DIR/writable/debugbar" \
"$INSTALL_DIR/public/assets/images"
# Set permissions
chown -R www-data:www-data "$INSTALL_DIR"
chmod -R 775 "$INSTALL_DIR/writable" "$INSTALL_DIR/public/assets/images"
# ------------------------------------------------------------------------------
# Step 3: Setup Environment (.env)
# ------------------------------------------------------------------------------
echo -e "${CYAN}--> Step 3: Configuring .env...${NC}"
cat <<EOF > "$INSTALL_DIR/.env"
CI_ENVIRONMENT = production
app.baseURL = 'https://${DOMAIN}/'
database.default.hostname = ${DB_HOST}
database.default.username = ${DB_USER}
database.default.password = ${DB_PASS}
database.default.database = ${DB_NAME}
database.default.DBDriver = MySQLi
database.default.port = ${DB_PORT}
database.default.DBDebug = false
setup.dbToolsKey = yourStrongSetupKey123
snomed.csnotk.baseUrl = https://csnotk.e-atria.in/csnoserv
snomed.csnotk.enabled = true
snomed.csnotk.timeoutSec = 10
ABDM_BRIDGE_PUBLIC_URL=https://abdm-bridge.e-atria.in
abdm.connector = dreamsoft
abdm.directBaseUrl = https://dev.abdm.gov.in/api/v3
abdm.directTimeoutSec = 30
BRIDGE_SYNC_URL = https://csnotk.e-atria.in/api/bridge
ABDM_BRIDGE_URL = https://abdm-bridge.e-atria.in/api/v1/bridge
BRIDGE_SYNC_TIMEOUT = 20
BRIDGE_SYNC_PROVIDER = bridge
EOF
chown www-data:www-data "$INSTALL_DIR/.env"
chmod 640 "$INSTALL_DIR/.env"
# ------------------------------------------------------------------------------
# Step 4: Setup Database & Run Spark Installer
# ------------------------------------------------------------------------------
echo -e "${CYAN}--> Step 4: Ensuring Database and running HMS Installer...${NC}"
# Create DB if it doesn't exist
MYSQL_CMD="mysql -h ${DB_HOST} -P ${DB_PORT} -u ${DB_USER}"
if [ -n "$DB_PASS" ]; then
MYSQL_CMD="${MYSQL_CMD} -p${DB_PASS}"
fi
$MYSQL_CMD -e "CREATE DATABASE IF NOT EXISTS \`${DB_NAME}\` CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;" || {
echo -e "${RED}[ERROR] Could not create database '${DB_NAME}'. Check MySQL credentials.${NC}"
exit 1
}
# Run CodeIgniter 4 HMS installer command
php spark hms:install \
--hospital-name="${HOSPITAL_NAME}" \
--admin-user="${ADMIN_USER}" \
--admin-email="${ADMIN_EMAIL}" \
--admin-password="${ADMIN_PASS}"
# ------------------------------------------------------------------------------
# Step 5: Configure Apache VirtualHost & SSL
# ------------------------------------------------------------------------------
echo -e "${CYAN}--> Step 5: Setting up Apache VirtualHost for ${DOMAIN}...${NC}"
VHOST_FILE="/etc/apache2/sites-available/${DOMAIN}.conf"
# Check if SSL certificates exist
HAS_SSL=false
if [ -f "$SSL_CERT" ] && [ -f "$SSL_KEY" ]; then
HAS_SSL=true
fi
if [ "$HAS_SSL" = true ]; then
echo -e "${GREEN}Using existing SSL certificate (${SSL_CERT})...${NC}"
cat <<EOF > "$VHOST_FILE"
<VirtualHost *:80>
ServerName ${DOMAIN}
Redirect permanent / https://${DOMAIN}/
</VirtualHost>
<VirtualHost *:443>
ServerName ${DOMAIN}
SSLEngine on
SSLCertificateFile ${SSL_CERT}
SSLCertificateKeyFile ${SSL_KEY}
DocumentRoot ${INSTALL_DIR}/public
<Directory ${INSTALL_DIR}/public>
Options +FollowSymLinks
AllowOverride All
Require all granted
DirectoryIndex index.php index.html index.htm
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php/\$1 [L]
</IfModule>
</Directory>
ErrorLog /var/log/apache2/${DOMAIN}_error.log
CustomLog /var/log/apache2/${DOMAIN}_access.log combined
</VirtualHost>
EOF
else
echo -e "${YELLOW}SSL cert not found at ${SSL_CERT}. Creating standard HTTP virtualhost...${NC}"
cat <<EOF > "$VHOST_FILE"
<VirtualHost *:80>
ServerName ${DOMAIN}
DocumentRoot ${INSTALL_DIR}/public
<Directory ${INSTALL_DIR}/public>
Options +FollowSymLinks
AllowOverride All
Require all granted
DirectoryIndex index.php index.html index.htm
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php/\$1 [L]
</IfModule>
</Directory>
ErrorLog /var/log/apache2/${DOMAIN}_error.log
CustomLog /var/log/apache2/${DOMAIN}_access.log combined
</VirtualHost>
EOF
fi
# Enable site
a2ensite "${DOMAIN}.conf"
apache2ctl configtest
systemctl reload apache2
# ------------------------------------------------------------------------------
# Step 6: Verify Endpoint
# ------------------------------------------------------------------------------
echo -e "${CYAN}--> Step 6: Testing website endpoint...${NC}"
HTTP_CODE=$(curl -k -s -o /dev/null -w "%{http_code}" "https://${DOMAIN}/login" || true)
if [ "$HTTP_CODE" = "200" ]; then
echo -e "${GREEN}${BOLD}[SUCCESS] HMS is successfully installed and responding (HTTP 200)!${NC}"
else
echo -e "${YELLOW}[NOTE] HTTP Code: ${HTTP_CODE}. Please verify Apache logs at /var/log/apache2/${DOMAIN}_error.log if needed.${NC}"
fi
echo -e "\n${GREEN}${BOLD}======================================================================${NC}"
echo -e "${GREEN}${BOLD} INSTALLATION COMPLETE! ${NC}"
echo -e "${GREEN}${BOLD}======================================================================${NC}"
echo -e " Web URL : https://${DOMAIN}/login"
echo -e " Hospital Name : ${HOSPITAL_NAME}"
echo -e " Database : ${DB_NAME}"
echo -e " Admin Username : ${ADMIN_USER}"
echo -e " Admin Password : ${ADMIN_PASS}"
echo -e " Install Path : ${INSTALL_DIR}"
echo -e "${GREEN}======================================================================${NC}\n"