LAB 55 (3 TIER APPLICATION SETUP) - arunsinghchauhan38-arch/CLOUDARCHITECTDOC GitHub Wiki

OCI Three-Tier Architecture

The Three-Tier Architecture is a widely adopted enterprise design pattern that separates applications into three logical layers:

  • Web Layer (Presentation Layer)

  • Application Layer (Business Logic Layer)

  • Database Layer (Data Layer)

This model enhances security, scalability, availability, and manageability in cloud environments.

πŸ— Architecture Flow with WAF

Internet β†’ DNS β†’ OCI WAF β†’ Public Load Balancer β†’ Backend Set β†’ Application Servers β†’ Database Servers

            Internet
                |
                |
         +------------+
         |    DNS     |
         +------------+
                |
                |
         +------------+
         |  OCI WAF   |
         +------------+
                |
                |
    +----------------------+
    | Public Load Balancer |
    |   (Public Subnet)    |
    +----------------------+
                |
                |
        +---------------+
        |  Backend Set  |
        +---------------+
                |
                |
+----------------------------------+
|      Application Servers         |
|    (App Subnet - Private)        |
+----------------------------------+
                |
                |
+----------------------------------+
|        Database Servers          |
|     (DB Subnet - Private)        |
+----------------------------------+
  • DNS resolves the application domain to the Load Balancer’s public IP.

  • OCI WAF inspects traffic, blocking malicious requests (SQL Injection, XSS, CSRF, DDoS).

  • Public Load Balancer distributes traffic across backend application servers.

  • Application Servers (private subnet) process business logic and API requests.

  • Database Servers (private subnet) handle persistent data storage and queries.

πŸ”§ OCI Components

image

πŸ”„ Traffic Flow

  1. User Access β†’ DNS resolves domain β†’ WAF filters malicious traffic β†’ request reaches Load Balancer.

  2. Load Balancer β†’ Listens on port 80/443, terminates SSL, performs health checks, forwards traffic to backend set.

  3. Backend Set β†’ Routes requests using policies (Round Robin, Least Connections, IP Hash).

  4. Application Servers β†’ Execute business logic, authentication, API processing.

  5. Database Servers β†’ Handle queries (MySQL 3306, Oracle 1521, PostgreSQL 5432).

  6. Response Returned β†’ Data flows back through Load Balancer β†’ User.

Create Subnets with Dedicated Route Tables and Security Lists

Create separate subnets for the Application Tier, Load Balancer Tier, and Database Tier, with a dedicated Route Table and Security List associated with each subnet.

asus_pvt_subnet
 β”œβ”€β”€ asus_rt
 └── asus_sl

asus_web_subnet
 β”œβ”€β”€ asus_web_rt
 └── asus_web_sub_sl

asus_db_subnet
 β”œβ”€β”€ asus_db_rt
 └── asus_db_sub_sl
image image image

Phase 1: Step-by-step guide to Host Application in Public Server

Step 1 β€” Connect to the Server

Connect to the server using SSH.

Step 2 β€” Install Apache Web Server

yum install httpd

Step 3 β€” Start and Enable Apache

systemctl start httpd

systemctl enable httpd

Verify:

systemctl status httpd

The status should show:

Active: active (running)

image

Step 4 β€” Create the Website Install ITSM App

  1. Go to html using below command
cd /var/www/html
  1. Create index file and paste python script
vi index.html

Add:

To go into Insert mode

Press i key

To Save the file

Esc+:wq! β†’ Save and exit.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Local ITSM Tool</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }

        body {
            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif;
            background: #f4f4f4;
            color: #333;
        }

        .header {
            background: #2c3e50;
            color: white;
            padding: 1rem 2rem;
            box-shadow: 0 2px 4px rgba(0,0,0,0.1);
        }

        .header h1 {
            font-size: 1.5rem;
            font-weight: 500;
        }

        .nav {
            background: #34495e;
            padding: 0.5rem 2rem;
            display: flex;
            gap: 1rem;
            box-shadow: 0 2px 4px rgba(0,0,0,0.1);
        }

        .nav button {
            background: transparent;
            color: white;
            border: none;
            padding: 0.75rem 1.5rem;
            cursor: pointer;
            font-size: 0.95rem;
            border-radius: 4px;
            transition: background 0.3s;
        }

        .nav button:hover {
            background: rgba(255,255,255,0.1);
        }

        .nav button.active {
            background: #3498db;
        }

        .container {
            max-width: 1400px;
            margin: 2rem auto;
            padding: 0 2rem;
        }

        .actions {
            display: flex;
            gap: 1rem;
            margin-bottom: 1.5rem;
        }

        .btn {
            padding: 0.75rem 1.5rem;
            border: none;
            border-radius: 4px;
            cursor: pointer;
            font-size: 0.95rem;
            transition: all 0.3s;
            font-weight: 500;
        }

        .btn-primary {
            background: #3498db;
            color: white;
        }

        .btn-primary:hover {
            background: #2980b9;
        }

        .btn-secondary {
            background: #95a5a6;
            color: white;
        }

        .btn-secondary:hover {
            background: #7f8c8d;
        }

        .btn-success {
            background: #27ae60;
            color: white;
        }

        .btn-success:hover {
            background: #229954;
        }

        .card {
            background: white;
            border-radius: 8px;
            box-shadow: 0 2px 8px rgba(0,0,0,0.1);
            overflow: hidden;
        }

        .card-header {
            background: #ecf0f1;
            padding: 1rem 1.5rem;
            border-bottom: 1px solid #ddd;
            font-weight: 600;
            font-size: 1.1rem;
        }

        .card-body {
            padding: 1.5rem;
        }

        table {
            width: 100%;
            border-collapse: collapse;
        }

        th {
            background: #ecf0f1;
            padding: 0.75rem;
            text-align: left;
            font-weight: 600;
            border-bottom: 2px solid #bdc3c7;
        }

        td {
            padding: 0.75rem;
            border-bottom: 1px solid #ecf0f1;
        }

        tr:hover {
            background: #f8f9fa;
        }

        .badge {
            padding: 0.25rem 0.75rem;
            border-radius: 12px;
            font-size: 0.85rem;
            font-weight: 500;
            display: inline-block;
        }

        .badge-new { background: #3498db; color: white; }
        .badge-inprogress { background: #f39c12; color: white; }
        .badge-resolved { background: #27ae60; color: white; }
        .badge-closed { background: #95a5a6; color: white; }
        .badge-pending { background: #e67e22; color: white; }
        .badge-approved { background: #27ae60; color: white; }
        .badge-rejected { background: #e74c3c; color: white; }
        .badge-draft { background: #95a5a6; color: white; }
        .badge-scheduled { background: #9b59b6; color: white; }
        .badge-implemented { background: #16a085; color: white; }

        .priority-critical { color: #e74c3c; font-weight: 600; }
        .priority-high { color: #e67e22; font-weight: 600; }
        .priority-medium { color: #f39c12; font-weight: 600; }
        .priority-low { color: #27ae60; font-weight: 600; }

        .modal {
            display: none;
            position: fixed;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            background: rgba(0,0,0,0.5);
            z-index: 1000;
            overflow-y: auto;
        }

        .modal.active {
            display: flex;
            align-items: center;
            justify-content: center;
            padding: 2rem;
        }

        .modal-content {
            background: white;
            border-radius: 8px;
            width: 100%;
            max-width: 800px;
            max-height: 90vh;
            overflow-y: auto;
        }

        .modal-header {
            padding: 1.5rem;
            border-bottom: 1px solid #ecf0f1;
            display: flex;
            justify-content: space-between;
            align-items: center;
        }

        .modal-header h2 {
            font-size: 1.5rem;
            font-weight: 600;
        }

        .modal-body {
            padding: 1.5rem;
        }

        .modal-footer {
            padding: 1.5rem;
            border-top: 1px solid #ecf0f1;
            display: flex;
            justify-content: flex-end;
            gap: 1rem;
        }

        .close-btn {
            background: none;
            border: none;
            font-size: 1.5rem;
            cursor: pointer;
            color: #7f8c8d;
        }

        .close-btn:hover {
            color: #2c3e50;
        }

        .form-group {
            margin-bottom: 1.5rem;
        }

        .form-group label {
            display: block;
            margin-bottom: 0.5rem;
            font-weight: 500;
            color: #2c3e50;
        }

        .form-group input,
        .form-group select,
        .form-group textarea {
            width: 100%;
            padding: 0.75rem;
            border: 1px solid #ddd;
            border-radius: 4px;
            font-size: 0.95rem;
            font-family: inherit;
        }

        .form-group textarea {
            min-height: 100px;
            resize: vertical;
        }

        .form-group input:focus,
        .form-group select:focus,
        .form-group textarea:focus {
            outline: none;
            border-color: #3498db;
            box-shadow: 0 0 0 3px rgba(52, 152, 219, 0.1);
        }

        .form-row {
            display: grid;
            grid-template-columns: 1fr 1fr;
            gap: 1rem;
        }

        .section-hidden {
            display: none;
        }

        .record-link {
            color: #3498db;
            text-decoration: none;
            cursor: pointer;
        }

        .record-link:hover {
            text-decoration: underline;
        }

        .stats {
            display: grid;
            grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
            gap: 1rem;
            margin-bottom: 2rem;
        }

        .stat-card {
            background: white;
            padding: 1.5rem;
            border-radius: 8px;
            box-shadow: 0 2px 8px rgba(0,0,0,0.1);
        }

        .stat-card h3 {
            font-size: 2rem;
            color: #3498db;
            margin-bottom: 0.5rem;
        }

        .stat-card p {
            color: #7f8c8d;
            font-size: 0.9rem;
        }

        .empty-state {
            text-align: center;
            padding: 3rem;
            color: #7f8c8d;
        }

        .empty-state-icon {
            font-size: 3rem;
            margin-bottom: 1rem;
        }

        .search-filter {
            margin-bottom: 1.5rem;
        }

        .search-filter input {
            width: 100%;
            padding: 0.75rem;
            border: 1px solid #ddd;
            border-radius: 4px;
            font-size: 0.95rem;
        }

        .action-buttons {
            display: flex;
            gap: 0.5rem;
        }

        .action-btn {
            padding: 0.25rem 0.75rem;
            font-size: 0.85rem;
            border-radius: 4px;
            border: none;
            cursor: pointer;
            transition: all 0.3s;
        }

        .action-btn-view {
            background: #3498db;
            color: white;
        }

        .action-btn-view:hover {
            background: #2980b9;
        }

        .action-btn-edit {
            background: #f39c12;
            color: white;
        }

        .action-btn-edit:hover {
            background: #e67e22;
        }

        .btn-danger {
            background: #e74c3c;
            color: white;
        }

        .btn-danger:hover {
            background: #c0392b;
        }

        .detail-view {
            display: grid;
            gap: 1rem;
        }

        .detail-row {
            display: grid;
            grid-template-columns: 200px 1fr;
            padding: 0.75rem 0;
            border-bottom: 1px solid #ecf0f1;
        }

        .detail-label {
            font-weight: 600;
            color: #2c3e50;
        }

        .detail-value {
            color: #555;
        }

        @media (max-width: 768px) {
            .form-row {
                grid-template-columns: 1fr;
            }

            .detail-row {
                grid-template-columns: 1fr;
                gap: 0.25rem;
            }

            .container {
                padding: 0 1rem;
            }

            .nav {
                overflow-x: auto;
            }
        }
    </style>
</head>
<body>
    <div class="header">
        <h1>πŸ”§ Local ITSM Tool</h1>
    </div>

    <div class="nav">
        <button class="active" onclick="switchSection('incidents')">Incidents</button>
        <button onclick="switchSection('ritm')">Request Items (RITM)</button>
        <button onclick="switchSection('changes')">Change Management</button>
        <button onclick="switchSection('dashboard')">Dashboard</button>
    </div>

    <div class="container">
        <!-- Dashboard Section -->
        <div id="dashboard-section" class="section-hidden">
            <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 1.5rem;">
                <h2 style="margin: 0; color: #2c3e50;">Dashboard Overview</h2>
                <button class="btn btn-danger" onclick="clearAllData()">πŸ—‘οΈ Clear All Data</button>
            </div>
            <div class="stats">
                <div class="stat-card">
                    <h3 id="stat-incidents">0</h3>
                    <p>Total Incidents</p>
                </div>
                <div class="stat-card">
                    <h3 id="stat-ritm">0</h3>
                    <p>Request Items</p>
                </div>
                <div class="stat-card">
                    <h3 id="stat-changes">0</h3>
                    <p>Changes</p>
                </div>
                <div class="stat-card">
                    <h3 id="stat-open">0</h3>
                    <p>Open Items</p>
                </div>
            </div>
        </div>

        <!-- Incidents Section -->
        <div id="incidents-section">
            <div class="actions">
                <button class="btn btn-primary" onclick="openCreateModal('incident')">+ Create New Incident</button>
                <button class="btn btn-secondary" onclick="exportData('incidents')">Export to JSON</button>
                <button class="btn btn-danger" onclick="clearAllRecords('incidents')">Clear All Records</button>
            </div>

            <div class="search-filter">
                <input type="text" placeholder="Search incidents..." oninput="filterTable('incidents', this.value)">
            </div>

            <div class="card">
                <div class="card-header">Incident List</div>
                <div class="card-body">
                    <table id="incidents-table">
                        <thead>
                            <tr>
                                <th>Number</th>
                                <th>Short Description</th>
                                <th>Priority</th>
                                <th>Status</th>
                                <th>Assigned To</th>
                                <th>Created</th>
                                <th>Actions</th>
                            </tr>
                        </thead>
                        <tbody id="incidents-tbody">
                            <tr>
                                <td colspan="7" class="empty-state">
                                    <div class="empty-state-icon">πŸ“‹</div>
                                    <p>No incidents found. Create your first incident to get started.</p>
                                </td>
                            </tr>
                        </tbody>
                    </table>
                </div>
            </div>
        </div>

        <!-- RITM Section -->
        <div id="ritm-section" class="section-hidden">
            <div class="actions">
                <button class="btn btn-primary" onclick="openCreateModal('ritm')">+ Create New Request Item</button>
                <button class="btn btn-secondary" onclick="exportData('ritm')">Export to JSON</button>
                <button class="btn btn-danger" onclick="clearAllRecords('ritm')">Clear All Records</button>
            </div>

            <div class="search-filter">
                <input type="text" placeholder="Search request items..." oninput="filterTable('ritm', this.value)">
            </div>

            <div class="card">
                <div class="card-header">Request Items (RITM) List</div>
                <div class="card-body">
                    <table id="ritm-table">
                        <thead>
                            <tr>
                                <th>Number</th>
                                <th>Short Description</th>
                                <th>Category</th>
                                <th>Status</th>
                                <th>Requested By</th>
                                <th>Created</th>
                                <th>Actions</th>
                            </tr>
                        </thead>
                        <tbody id="ritm-tbody">
                            <tr>
                                <td colspan="7" class="empty-state">
                                    <div class="empty-state-icon">πŸ“¦</div>
                                    <p>No request items found. Create your first RITM to get started.</p>
                                </td>
                            </tr>
                        </tbody>
                    </table>
                </div>
            </div>
        </div>

        <!-- Changes Section -->
        <div id="changes-section" class="section-hidden">
            <div class="actions">
                <button class="btn btn-primary" onclick="openCreateModal('change')">+ Create New Change</button>
                <button class="btn btn-secondary" onclick="exportData('changes')">Export to JSON</button>
                <button class="btn btn-danger" onclick="clearAllRecords('changes')">Clear All Records</button>
            </div>

            <div class="search-filter">
                <input type="text" placeholder="Search changes..." oninput="filterTable('changes', this.value)">
            </div>

            <div class="card">
                <div class="card-header">Change Management List</div>
                <div class="card-body">
                    <table id="changes-table">
                        <thead>
                            <tr>
                                <th>Number</th>
                                <th>Short Description</th>
                                <th>Type</th>
                                <th>Risk</th>
                                <th>Status</th>
                                <th>Scheduled Date</th>
                                <th>Actions</th>
                            </tr>
                        </thead>
                        <tbody id="changes-tbody">
                            <tr>
                                <td colspan="7" class="empty-state">
                                    <div class="empty-state-icon">πŸ”„</div>
                                    <p>No changes found. Create your first change request to get started.</p>
                                </td>
                            </tr>
                        </tbody>
                    </table>
                </div>
            </div>
        </div>
    </div>

    <!-- Create/Edit Modal -->
    <div id="modal" class="modal">
        <div class="modal-content">
            <div class="modal-header">
                <h2 id="modal-title">Create New Incident</h2>
                <button class="close-btn" onclick="closeModal()">&times;</button>
            </div>
            <div class="modal-body" id="modal-body">
                <!-- Form will be dynamically inserted here -->
            </div>
            <div class="modal-footer">
                <button class="btn btn-secondary" onclick="closeModal()">Cancel</button>
                <button class="btn btn-success" id="save-btn" onclick="saveRecord()">Save</button>
            </div>
        </div>
    </div>

    <!-- View Modal -->
    <div id="view-modal" class="modal">
        <div class="modal-content">
            <div class="modal-header">
                <h2 id="view-modal-title">View Record</h2>
                <button class="close-btn" onclick="closeViewModal()">&times;</button>
            </div>
            <div class="modal-body" id="view-modal-body">
                <!-- Details will be dynamically inserted here -->
            </div>
            <div class="modal-footer">
                <button class="btn btn-secondary" onclick="closeViewModal()">Close</button>
                <button class="btn btn-primary" id="edit-from-view-btn" onclick="editFromView()">Edit</button>
            </div>
        </div>
    </div>

    <script>
        // Data Storage
        let incidents = JSON.parse(localStorage.getItem('itsm_incidents') || '[]');
        let ritms = JSON.parse(localStorage.getItem('itsm_ritms') || '[]');
        let changes = JSON.parse(localStorage.getItem('itsm_changes') || '[]');
        
        let currentSection = 'incidents';
        let currentRecordType = null;
        let currentRecordId = null;
        let viewingRecord = null;

        // Counter for IDs
        let incidentCounter = parseInt(localStorage.getItem('incident_counter') || '1000');
        let ritmCounter = parseInt(localStorage.getItem('ritm_counter') || '2000');
        let changeCounter = parseInt(localStorage.getItem('change_counter') || '3000');

        // Initialize the app
        function init() {
            renderIncidents();
            renderRITMs();
            renderChanges();
            updateDashboard();
        }

        // Switch between sections
        function switchSection(section) {
            // Hide all sections
            document.getElementById('dashboard-section').classList.add('section-hidden');
            document.getElementById('incidents-section').classList.add('section-hidden');
            document.getElementById('ritm-section').classList.add('section-hidden');
            document.getElementById('changes-section').classList.add('section-hidden');

            // Show selected section
            if (section === 'dashboard') {
                document.getElementById('dashboard-section').classList.remove('section-hidden');
                updateDashboard();
            } else if (section === 'incidents') {
                document.getElementById('incidents-section').classList.remove('section-hidden');
            } else if (section === 'ritm') {
                document.getElementById('ritm-section').classList.remove('section-hidden');
            } else if (section === 'changes') {
                document.getElementById('changes-section').classList.remove('section-hidden');
            }

            // Update nav active state
            document.querySelectorAll('.nav button').forEach(btn => {
                btn.classList.remove('active');
            });
            event.target.classList.add('active');
            
            currentSection = section;
        }

        // Update Dashboard
        function updateDashboard() {
            document.getElementById('stat-incidents').textContent = incidents.length;
            document.getElementById('stat-ritm').textContent = ritms.length;
            document.getElementById('stat-changes').textContent = changes.length;
            
            const openIncidents = incidents.filter(i => i.status !== 'Closed').length;
            const openRitms = ritms.filter(r => r.status !== 'Closed').length;
            const openChanges = changes.filter(c => c.status !== 'Implemented' && c.status !== 'Closed').length;
            
            document.getElementById('stat-open').textContent = openIncidents + openRitms + openChanges;
        }

        // Open Create/Edit Modal
        function openCreateModal(type, recordId = null) {
            currentRecordType = type;
            currentRecordId = recordId;
            
            const modal = document.getElementById('modal');
            const modalTitle = document.getElementById('modal-title');
            const modalBody = document.getElementById('modal-body');
            
            let title = '';
            let formHTML = '';
            
            if (type === 'incident') {
                title = recordId ? 'Edit Incident' : 'Create New Incident';
                formHTML = getIncidentForm(recordId);
            } else if (type === 'ritm') {
                title = recordId ? 'Edit Request Item' : 'Create New Request Item';
                formHTML = getRITMForm(recordId);
            } else if (type === 'change') {
                title = recordId ? 'Edit Change' : 'Create New Change';
                formHTML = getChangeForm(recordId);
            }
            
            modalTitle.textContent = title;
            modalBody.innerHTML = formHTML;
            modal.classList.add('active');
        }

        // Close Modal
        function closeModal() {
            document.getElementById('modal').classList.remove('active');
            currentRecordType = null;
            currentRecordId = null;
        }

        // Incident Form
        function getIncidentForm(id) {
            let record = id ? incidents.find(i => i.id === id) : {};
            
            return `
                <div class="form-row">
                    <div class="form-group">
                        <label>Short Description *</label>
                        <input type="text" id="short_description" value="${record.short_description || ''}" required>
                    </div>
                    <div class="form-group">
                        <label>Priority *</label>
                        <select id="priority">
                            <option value="Critical" ${record.priority === 'Critical' ? 'selected' : ''}>Critical</option>
                            <option value="High" ${record.priority === 'High' ? 'selected' : ''}>High</option>
                            <option value="Medium" ${record.priority === 'Medium' || !record.priority ? 'selected' : ''}>Medium</option>
                            <option value="Low" ${record.priority === 'Low' ? 'selected' : ''}>Low</option>
                        </select>
                    </div>
                </div>
                <div class="form-row">
                    <div class="form-group">
                        <label>Status *</label>
                        <select id="status">
                            <option value="New" ${record.status === 'New' || !record.status ? 'selected' : ''}>New</option>
                            <option value="In Progress" ${record.status === 'In Progress' ? 'selected' : ''}>In Progress</option>
                            <option value="Resolved" ${record.status === 'Resolved' ? 'selected' : ''}>Resolved</option>
                            <option value="Closed" ${record.status === 'Closed' ? 'selected' : ''}>Closed</option>
                        </select>
                    </div>
                    <div class="form-group">
                        <label>Assigned To</label>
                        <input type="text" id="assigned_to" value="${record.assigned_to || ''}">
                    </div>
                </div>
                <div class="form-row">
                    <div class="form-group">
                        <label>Category</label>
                        <select id="category">
                            <option value="">-- Select --</option>
                            <option value="Hardware" ${record.category === 'Hardware' ? 'selected' : ''}>Hardware</option>
                            <option value="Software" ${record.category === 'Software' ? 'selected' : ''}>Software</option>
                            <option value="Network" ${record.category === 'Network' ? 'selected' : ''}>Network</option>
                            <option value="Database" ${record.category === 'Database' ? 'selected' : ''}>Database</option>
                            <option value="Security" ${record.category === 'Security' ? 'selected' : ''}>Security</option>
                            <option value="Other" ${record.category === 'Other' ? 'selected' : ''}>Other</option>
                        </select>
                    </div>
                    <div class="form-group">
                        <label>Caller</label>
                        <input type="text" id="caller" value="${record.caller || ''}">
                    </div>
                </div>
                <div class="form-group">
                    <label>Description</label>
                    <textarea id="description">${record.description || ''}</textarea>
                </div>
                <div class="form-group">
                    <label>Resolution Notes</label>
                    <textarea id="resolution_notes">${record.resolution_notes || ''}</textarea>
                </div>
            `;
        }

        // RITM Form
        function getRITMForm(id) {
            let record = id ? ritms.find(r => r.id === id) : {};
            
            return `
                <div class="form-row">
                    <div class="form-group">
                        <label>Short Description *</label>
                        <input type="text" id="short_description" value="${record.short_description || ''}" required>
                    </div>
                    <div class="form-group">
                        <label>Category *</label>
                        <select id="category">
                            <option value="Hardware Request" ${record.category === 'Hardware Request' || !record.category ? 'selected' : ''}>Hardware Request</option>
                            <option value="Software Request" ${record.category === 'Software Request' ? 'selected' : ''}>Software Request</option>
                            <option value="Access Request" ${record.category === 'Access Request' ? 'selected' : ''}>Access Request</option>
                            <option value="Service Request" ${record.category === 'Service Request' ? 'selected' : ''}>Service Request</option>
                            <option value="Other" ${record.category === 'Other' ? 'selected' : ''}>Other</option>
                        </select>
                    </div>
                </div>
                <div class="form-row">
                    <div class="form-group">
                        <label>Status *</label>
                        <select id="status">
                            <option value="Pending Approval" ${record.status === 'Pending Approval' || !record.status ? 'selected' : ''}>Pending Approval</option>
                            <option value="Approved" ${record.status === 'Approved' ? 'selected' : ''}>Approved</option>
                            <option value="Rejected" ${record.status === 'Rejected' ? 'selected' : ''}>Rejected</option>
                            <option value="In Progress" ${record.status === 'In Progress' ? 'selected' : ''}>In Progress</option>
                            <option value="Completed" ${record.status === 'Completed' ? 'selected' : ''}>Completed</option>
                            <option value="Closed" ${record.status === 'Closed' ? 'selected' : ''}>Closed</option>
                        </select>
                    </div>
                    <div class="form-group">
                        <label>Requested By</label>
                        <input type="text" id="requested_by" value="${record.requested_by || ''}">
                    </div>
                </div>
                <div class="form-row">
                    <div class="form-group">
                        <label>Assigned To</label>
                        <input type="text" id="assigned_to" value="${record.assigned_to || ''}">
                    </div>
                    <div class="form-group">
                        <label>Due Date</label>
                        <input type="date" id="due_date" value="${record.due_date || ''}">
                    </div>
                </div>
                <div class="form-group">
                    <label>Description</label>
                    <textarea id="description">${record.description || ''}</textarea>
                </div>
                <div class="form-group">
                    <label>Fulfillment Notes</label>
                    <textarea id="fulfillment_notes">${record.fulfillment_notes || ''}</textarea>
                </div>
            `;
        }

        // Change Form
        function getChangeForm(id) {
            let record = id ? changes.find(c => c.id === id) : {};
            
            return `
                <div class="form-row">
                    <div class="form-group">
                        <label>Short Description *</label>
                        <input type="text" id="short_description" value="${record.short_description || ''}" required>
                    </div>
                    <div class="form-group">
                        <label>Type *</label>
                        <select id="type">
                            <option value="Standard" ${record.type === 'Standard' || !record.type ? 'selected' : ''}>Standard</option>
                            <option value="Normal" ${record.type === 'Normal' ? 'selected' : ''}>Normal</option>
                            <option value="Emergency" ${record.type === 'Emergency' ? 'selected' : ''}>Emergency</option>
                        </select>
                    </div>
                </div>
                <div class="form-row">
                    <div class="form-group">
                        <label>Risk *</label>
                        <select id="risk">
                            <option value="Low" ${record.risk === 'Low' || !record.risk ? 'selected' : ''}>Low</option>
                            <option value="Medium" ${record.risk === 'Medium' ? 'selected' : ''}>Medium</option>
                            <option value="High" ${record.risk === 'High' ? 'selected' : ''}>High</option>
                            <option value="Critical" ${record.risk === 'Critical' ? 'selected' : ''}>Critical</option>
                        </select>
                    </div>
                    <div class="form-group">
                        <label>Status *</label>
                        <select id="status">
                            <option value="Draft" ${record.status === 'Draft' || !record.status ? 'selected' : ''}>Draft</option>
                            <option value="Pending Approval" ${record.status === 'Pending Approval' ? 'selected' : ''}>Pending Approval</option>
                            <option value="Approved" ${record.status === 'Approved' ? 'selected' : ''}>Approved</option>
                            <option value="Scheduled" ${record.status === 'Scheduled' ? 'selected' : ''}>Scheduled</option>
                            <option value="In Progress" ${record.status === 'In Progress' ? 'selected' : ''}>In Progress</option>
                            <option value="Implemented" ${record.status === 'Implemented' ? 'selected' : ''}>Implemented</option>
                            <option value="Closed" ${record.status === 'Closed' ? 'selected' : ''}>Closed</option>
                        </select>
                    </div>
                </div>
                <div class="form-row">
                    <div class="form-group">
                        <label>Requested By</label>
                        <input type="text" id="requested_by" value="${record.requested_by || ''}">
                    </div>
                    <div class="form-group">
                        <label>Assigned To</label>
                        <input type="text" id="assigned_to" value="${record.assigned_to || ''}">
                    </div>
                </div>
                <div class="form-row">
                    <div class="form-group">
                        <label>Scheduled Start Date</label>
                        <input type="datetime-local" id="scheduled_start" value="${record.scheduled_start || ''}">
                    </div>
                    <div class="form-group">
                        <label>Scheduled End Date</label>
                        <input type="datetime-local" id="scheduled_end" value="${record.scheduled_end || ''}">
                    </div>
                </div>
                <div class="form-group">
                    <label>Description</label>
                    <textarea id="description">${record.description || ''}</textarea>
                </div>
                <div class="form-group">
                    <label>Implementation Plan</label>
                    <textarea id="implementation_plan">${record.implementation_plan || ''}</textarea>
                </div>
                <div class="form-group">
                    <label>Backout Plan</label>
                    <textarea id="backout_plan">${record.backout_plan || ''}</textarea>
                </div>
                <div class="form-group">
                    <label>Test Plan</label>
                    <textarea id="test_plan">${record.test_plan || ''}</textarea>
                </div>
            `;
        }

        // Save Record
        function saveRecord() {
            const short_description = document.getElementById('short_description').value.trim();
            
            if (!short_description) {
                alert('Please fill in the required fields');
                return;
            }
            
            if (currentRecordType === 'incident') {
                saveIncident();
            } else if (currentRecordType === 'ritm') {
                saveRITM();
            } else if (currentRecordType === 'change') {
                saveChange();
            }
            
            closeModal();
        }

        // Save Incident
        function saveIncident() {
            const record = {
                id: currentRecordId || `INC${incidentCounter++}`,
                number: currentRecordId || `INC${incidentCounter - 1}`,
                short_description: document.getElementById('short_description').value,
                priority: document.getElementById('priority').value,
                status: document.getElementById('status').value,
                assigned_to: document.getElementById('assigned_to').value,
                category: document.getElementById('category').value,
                caller: document.getElementById('caller').value,
                description: document.getElementById('description').value,
                resolution_notes: document.getElementById('resolution_notes').value,
                created: currentRecordId ? incidents.find(i => i.id === currentRecordId).created : new Date().toISOString(),
                updated: new Date().toISOString()
            };
            
            if (currentRecordId) {
                const index = incidents.findIndex(i => i.id === currentRecordId);
                incidents[index] = record;
            } else {
                incidents.push(record);
                localStorage.setItem('incident_counter', incidentCounter);
            }
            
            localStorage.setItem('itsm_incidents', JSON.stringify(incidents));
            renderIncidents();
            updateDashboard();
        }

        // Save RITM
        function saveRITM() {
            const record = {
                id: currentRecordId || `RITM${ritmCounter++}`,
                number: currentRecordId || `RITM${ritmCounter - 1}`,
                short_description: document.getElementById('short_description').value,
                category: document.getElementById('category').value,
                status: document.getElementById('status').value,
                requested_by: document.getElementById('requested_by').value,
                assigned_to: document.getElementById('assigned_to').value,
                due_date: document.getElementById('due_date').value,
                description: document.getElementById('description').value,
                fulfillment_notes: document.getElementById('fulfillment_notes').value,
                created: currentRecordId ? ritms.find(r => r.id === currentRecordId).created : new Date().toISOString(),
                updated: new Date().toISOString()
            };
            
            if (currentRecordId) {
                const index = ritms.findIndex(r => r.id === currentRecordId);
                ritms[index] = record;
            } else {
                ritms.push(record);
                localStorage.setItem('ritm_counter', ritmCounter);
            }
            
            localStorage.setItem('itsm_ritms', JSON.stringify(ritms));
            renderRITMs();
            updateDashboard();
        }

        // Save Change
        function saveChange() {
            const record = {
                id: currentRecordId || `CHG${changeCounter++}`,
                number: currentRecordId || `CHG${changeCounter - 1}`,
                short_description: document.getElementById('short_description').value,
                type: document.getElementById('type').value,
                risk: document.getElementById('risk').value,
                status: document.getElementById('status').value,
                requested_by: document.getElementById('requested_by').value,
                assigned_to: document.getElementById('assigned_to').value,
                scheduled_start: document.getElementById('scheduled_start').value,
                scheduled_end: document.getElementById('scheduled_end').value,
                description: document.getElementById('description').value,
                implementation_plan: document.getElementById('implementation_plan').value,
                backout_plan: document.getElementById('backout_plan').value,
                test_plan: document.getElementById('test_plan').value,
                created: currentRecordId ? changes.find(c => c.id === currentRecordId).created : new Date().toISOString(),
                updated: new Date().toISOString()
            };
            
            if (currentRecordId) {
                const index = changes.findIndex(c => c.id === currentRecordId);
                changes[index] = record;
            } else {
                changes.push(record);
                localStorage.setItem('change_counter', changeCounter);
            }
            
            localStorage.setItem('itsm_changes', JSON.stringify(changes));
            renderChanges();
            updateDashboard();
        }

        // Render Incidents
        function renderIncidents() {
            const tbody = document.getElementById('incidents-tbody');
            
            if (incidents.length === 0) {
                tbody.innerHTML = `
                    <tr>
                        <td colspan="7" class="empty-state">
                            <div class="empty-state-icon">πŸ“‹</div>
                            <p>No incidents found. Create your first incident to get started.</p>
                        </td>
                    </tr>
                `;
                return;
            }
            
            tbody.innerHTML = incidents.map(inc => `
                <tr>
                    <td><a class="record-link" onclick="viewRecord('incident', '${inc.id}')">${inc.number}</a></td>
                    <td>${inc.short_description}</td>
                    <td><span class="priority-${inc.priority.toLowerCase()}">${inc.priority}</span></td>
                    <td><span class="badge badge-${inc.status.toLowerCase().replace(' ', '')}">${inc.status}</span></td>
                    <td>${inc.assigned_to || '-'}</td>
                    <td>${formatDate(inc.created)}</td>
                    <td>
                        <div class="action-buttons">
                            <button class="action-btn action-btn-view" onclick="viewRecord('incident', '${inc.id}')">View</button>
                            <button class="action-btn action-btn-edit" onclick="openCreateModal('incident', '${inc.id}')">Edit</button>
                        </div>
                    </td>
                </tr>
            `).join('');
        }

        // Render RITMs
        function renderRITMs() {
            const tbody = document.getElementById('ritm-tbody');
            
            if (ritms.length === 0) {
                tbody.innerHTML = `
                    <tr>
                        <td colspan="7" class="empty-state">
                            <div class="empty-state-icon">πŸ“¦</div>
                            <p>No request items found. Create your first RITM to get started.</p>
                        </td>
                    </tr>
                `;
                return;
            }
            
            tbody.innerHTML = ritms.map(ritm => `
                <tr>
                    <td><a class="record-link" onclick="viewRecord('ritm', '${ritm.id}')">${ritm.number}</a></td>
                    <td>${ritm.short_description}</td>
                    <td>${ritm.category}</td>
                    <td><span class="badge badge-${ritm.status.toLowerCase().replace(' ', '')}">${ritm.status}</span></td>
                    <td>${ritm.requested_by || '-'}</td>
                    <td>${formatDate(ritm.created)}</td>
                    <td>
                        <div class="action-buttons">
                            <button class="action-btn action-btn-view" onclick="viewRecord('ritm', '${ritm.id}')">View</button>
                            <button class="action-btn action-btn-edit" onclick="openCreateModal('ritm', '${ritm.id}')">Edit</button>
                        </div>
                    </td>
                </tr>
            `).join('');
        }

        // Render Changes
        function renderChanges() {
            const tbody = document.getElementById('changes-tbody');
            
            if (changes.length === 0) {
                tbody.innerHTML = `
                    <tr>
                        <td colspan="7" class="empty-state">
                            <div class="empty-state-icon">πŸ”„</div>
                            <p>No changes found. Create your first change request to get started.</p>
                        </td>
                    </tr>
                `;
                return;
            }
            
            tbody.innerHTML = changes.map(chg => `
                <tr>
                    <td><a class="record-link" onclick="viewRecord('change', '${chg.id}')">${chg.number}</a></td>
                    <td>${chg.short_description}</td>
                    <td>${chg.type}</td>
                    <td><span class="priority-${chg.risk.toLowerCase()}">${chg.risk}</span></td>
                    <td><span class="badge badge-${chg.status.toLowerCase().replace(' ', '')}">${chg.status}</span></td>
                    <td>${chg.scheduled_start ? formatDate(chg.scheduled_start) : '-'}</td>
                    <td>
                        <div class="action-buttons">
                            <button class="action-btn action-btn-view" onclick="viewRecord('change', '${chg.id}')">View</button>
                            <button class="action-btn action-btn-edit" onclick="openCreateModal('change', '${chg.id}')">Edit</button>
                        </div>
                    </td>
                </tr>
            `).join('');
        }

        // View Record
        function viewRecord(type, id) {
            viewingRecord = { type, id };
            const modal = document.getElementById('view-modal');
            const modalTitle = document.getElementById('view-modal-title');
            const modalBody = document.getElementById('view-modal-body');
            
            let record, detailsHTML;
            
            if (type === 'incident') {
                record = incidents.find(i => i.id === id);
                modalTitle.textContent = `Incident: ${record.number}`;
                detailsHTML = `
                    <div class="detail-view">
                        <div class="detail-row">
                            <div class="detail-label">Number</div>
                            <div class="detail-value">${record.number}</div>
                        </div>
                        <div class="detail-row">
                            <div class="detail-label">Short Description</div>
                            <div class="detail-value">${record.short_description}</div>
                        </div>
                        <div class="detail-row">
                            <div class="detail-label">Priority</div>
                            <div class="detail-value"><span class="priority-${record.priority.toLowerCase()}">${record.priority}</span></div>
                        </div>
                        <div class="detail-row">
                            <div class="detail-label">Status</div>
                            <div class="detail-value"><span class="badge badge-${record.status.toLowerCase().replace(' ', '')}">${record.status}</span></div>
                        </div>
                        <div class="detail-row">
                            <div class="detail-label">Category</div>
                            <div class="detail-value">${record.category || '-'}</div>
                        </div>
                        <div class="detail-row">
                            <div class="detail-label">Assigned To</div>
                            <div class="detail-value">${record.assigned_to || '-'}</div>
                        </div>
                        <div class="detail-row">
                            <div class="detail-label">Caller</div>
                            <div class="detail-value">${record.caller || '-'}</div>
                        </div>
                        <div class="detail-row">
                            <div class="detail-label">Description</div>
                            <div class="detail-value">${record.description || '-'}</div>
                        </div>
                        <div class="detail-row">
                            <div class="detail-label">Resolution Notes</div>
                            <div class="detail-value">${record.resolution_notes || '-'}</div>
                        </div>
                        <div class="detail-row">
                            <div class="detail-label">Created</div>
                            <div class="detail-value">${formatDate(record.created)}</div>
                        </div>
                        <div class="detail-row">
                            <div class="detail-label">Last Updated</div>
                            <div class="detail-value">${formatDate(record.updated)}</div>
                        </div>
                    </div>
                `;
            } else if (type === 'ritm') {
                record = ritms.find(r => r.id === id);
                modalTitle.textContent = `Request Item: ${record.number}`;
                detailsHTML = `
                    <div class="detail-view">
                        <div class="detail-row">
                            <div class="detail-label">Number</div>
                            <div class="detail-value">${record.number}</div>
                        </div>
                        <div class="detail-row">
                            <div class="detail-label">Short Description</div>
                            <div class="detail-value">${record.short_description}</div>
                        </div>
                        <div class="detail-row">
                            <div class="detail-label">Category</div>
                            <div class="detail-value">${record.category}</div>
                        </div>
                        <div class="detail-row">
                            <div class="detail-label">Status</div>
                            <div class="detail-value"><span class="badge badge-${record.status.toLowerCase().replace(' ', '')}">${record.status}</span></div>
                        </div>
                        <div class="detail-row">
                            <div class="detail-label">Requested By</div>
                            <div class="detail-value">${record.requested_by || '-'}</div>
                        </div>
                        <div class="detail-row">
                            <div class="detail-label">Assigned To</div>
                            <div class="detail-value">${record.assigned_to || '-'}</div>
                        </div>
                        <div class="detail-row">
                            <div class="detail-label">Due Date</div>
                            <div class="detail-value">${record.due_date ? formatDate(record.due_date) : '-'}</div>
                        </div>
                        <div class="detail-row">
                            <div class="detail-label">Description</div>
                            <div class="detail-value">${record.description || '-'}</div>
                        </div>
                        <div class="detail-row">
                            <div class="detail-label">Fulfillment Notes</div>
                            <div class="detail-value">${record.fulfillment_notes || '-'}</div>
                        </div>
                        <div class="detail-row">
                            <div class="detail-label">Created</div>
                            <div class="detail-value">${formatDate(record.created)}</div>
                        </div>
                        <div class="detail-row">
                            <div class="detail-label">Last Updated</div>
                            <div class="detail-value">${formatDate(record.updated)}</div>
                        </div>
                    </div>
                `;
            } else if (type === 'change') {
                record = changes.find(c => c.id === id);
                modalTitle.textContent = `Change: ${record.number}`;
                detailsHTML = `
                    <div class="detail-view">
                        <div class="detail-row">
                            <div class="detail-label">Number</div>
                            <div class="detail-value">${record.number}</div>
                        </div>
                        <div class="detail-row">
                            <div class="detail-label">Short Description</div>
                            <div class="detail-value">${record.short_description}</div>
                        </div>
                        <div class="detail-row">
                            <div class="detail-label">Type</div>
                            <div class="detail-value">${record.type}</div>
                        </div>
                        <div class="detail-row">
                            <div class="detail-label">Risk</div>
                            <div class="detail-value"><span class="priority-${record.risk.toLowerCase()}">${record.risk}</span></div>
                        </div>
                        <div class="detail-row">
                            <div class="detail-label">Status</div>
                            <div class="detail-value"><span class="badge badge-${record.status.toLowerCase().replace(' ', '')}">${record.status}</span></div>
                        </div>
                        <div class="detail-row">
                            <div class="detail-label">Requested By</div>
                            <div class="detail-value">${record.requested_by || '-'}</div>
                        </div>
                        <div class="detail-row">
                            <div class="detail-label">Assigned To</div>
                            <div class="detail-value">${record.assigned_to || '-'}</div>
                        </div>
                        <div class="detail-row">
                            <div class="detail-label">Scheduled Start</div>
                            <div class="detail-value">${record.scheduled_start ? formatDate(record.scheduled_start) : '-'}</div>
                        </div>
                        <div class="detail-row">
                            <div class="detail-label">Scheduled End</div>
                            <div class="detail-value">${record.scheduled_end ? formatDate(record.scheduled_end) : '-'}</div>
                        </div>
                        <div class="detail-row">
                            <div class="detail-label">Description</div>
                            <div class="detail-value">${record.description || '-'}</div>
                        </div>
                        <div class="detail-row">
                            <div class="detail-label">Implementation Plan</div>
                            <div class="detail-value">${record.implementation_plan || '-'}</div>
                        </div>
                        <div class="detail-row">
                            <div class="detail-label">Backout Plan</div>
                            <div class="detail-value">${record.backout_plan || '-'}</div>
                        </div>
                        <div class="detail-row">
                            <div class="detail-label">Test Plan</div>
                            <div class="detail-value">${record.test_plan || '-'}</div>
                        </div>
                        <div class="detail-row">
                            <div class="detail-label">Created</div>
                            <div class="detail-value">${formatDate(record.created)}</div>
                        </div>
                        <div class="detail-row">
                            <div class="detail-label">Last Updated</div>
                            <div class="detail-value">${formatDate(record.updated)}</div>
                        </div>
                    </div>
                `;
            }
            
            modalBody.innerHTML = detailsHTML;
            modal.classList.add('active');
        }

        // Close View Modal
        function closeViewModal() {
            document.getElementById('view-modal').classList.remove('active');
            viewingRecord = null;
        }

        // Edit from View
        function editFromView() {
            if (viewingRecord) {
                closeViewModal();
                openCreateModal(viewingRecord.type, viewingRecord.id);
            }
        }

        // Filter Table
        function filterTable(type, searchTerm) {
            const term = searchTerm.toLowerCase();
            let data;
            
            if (type === 'incidents') {
                data = incidents;
            } else if (type === 'ritm') {
                data = ritms;
            } else if (type === 'changes') {
                data = changes;
            }
            
            const filtered = data.filter(record => {
                return record.short_description.toLowerCase().includes(term) ||
                       record.number.toLowerCase().includes(term) ||
                       (record.status && record.status.toLowerCase().includes(term));
            });
            
            // Re-render with filtered data
            const originalData = type === 'incidents' ? incidents : type === 'ritm' ? ritms : changes;
            if (type === 'incidents') {
                incidents = filtered;
                renderIncidents();
                incidents = originalData;
            } else if (type === 'ritm') {
                ritms = filtered;
                renderRITMs();
                ritms = originalData;
            } else if (type === 'changes') {
                changes = filtered;
                renderChanges();
                changes = originalData;
            }
        }

        // Export Data
        function exportData(type) {
            let data, filename;
            
            if (type === 'incidents') {
                data = incidents;
                filename = 'incidents.json';
            } else if (type === 'ritm') {
                data = ritms;
                filename = 'ritms.json';
            } else if (type === 'changes') {
                data = changes;
                filename = 'changes.json';
            }
            
            const dataStr = JSON.stringify(data, null, 2);
            const dataBlob = new Blob([dataStr], { type: 'application/json' });
            const url = URL.createObjectURL(dataBlob);
            const link = document.createElement('a');
            link.href = url;
            link.download = filename;
            link.click();
        }

        // Clear All Records
        function clearAllRecords(type) {
            let typeName, count;
            
            if (type === 'incidents') {
                typeName = 'Incidents';
                count = incidents.length;
            } else if (type === 'ritm') {
                typeName = 'Request Items';
                count = ritms.length;
            } else if (type === 'changes') {
                typeName = 'Changes';
                count = changes.length;
            }
            
            if (count === 0) {
                alert('There are no records to delete.');
                return;
            }
            
            const confirmed = confirm(
                `⚠️ WARNING: This will permanently delete all ${count} ${typeName}.\n\n` +
                `This action cannot be undone!\n\n` +
                `Click OK to proceed with deletion, or Cancel to keep your records.`
            );
            
            if (confirmed) {
                // Double confirmation for safety
                const doubleConfirm = confirm(
                    `Are you absolutely sure?\n\n` +
                    `This is your last chance to cancel before deleting ${count} ${typeName}.`
                );
                
                if (doubleConfirm) {
                    if (type === 'incidents') {
                        incidents = [];
                        localStorage.setItem('itsm_incidents', JSON.stringify(incidents));
                        renderIncidents();
                    } else if (type === 'ritm') {
                        ritms = [];
                        localStorage.setItem('itsm_ritms', JSON.stringify(ritms));
                        renderRITMs();
                    } else if (type === 'changes') {
                        changes = [];
                        localStorage.setItem('itsm_changes', JSON.stringify(changes));
                        renderChanges();
                    }
                    
                    updateDashboard();
                    alert(`βœ“ Successfully deleted all ${typeName}.`);
                }
            }
        }

        // Clear All Data (Everything)
        function clearAllData() {
            const totalRecords = incidents.length + ritms.length + changes.length;
            
            if (totalRecords === 0) {
                alert('There are no records to delete.');
                return;
            }
            
            const confirmed = confirm(
                `⚠️ DANGER: This will permanently delete ALL data:\n\n` +
                `β€’ ${incidents.length} Incidents\n` +
                `β€’ ${ritms.length} Request Items\n` +
                `β€’ ${changes.length} Changes\n\n` +
                `Total: ${totalRecords} records\n\n` +
                `This action CANNOT be undone!\n\n` +
                `Click OK to proceed with deletion, or Cancel to keep your data.`
            );
            
            if (confirmed) {
                // Triple confirmation for clearing everything
                const doubleConfirm = confirm(
                    `🚨 FINAL WARNING 🚨\n\n` +
                    `You are about to DELETE EVERYTHING (${totalRecords} records).\n\n` +
                    `This is your LAST CHANCE to cancel!\n\n` +
                    `Type anything in the prompt that appears next to confirm.`
                );
                
                if (doubleConfirm) {
                    const finalConfirm = prompt(
                        `To confirm deletion of ALL ${totalRecords} records, type: DELETE`
                    );
                    
                    if (finalConfirm === 'DELETE') {
                        // Clear all data
                        incidents = [];
                        ritms = [];
                        changes = [];
                        
                        // Clear localStorage
                        localStorage.setItem('itsm_incidents', JSON.stringify(incidents));
                        localStorage.setItem('itsm_ritms', JSON.stringify(ritms));
                        localStorage.setItem('itsm_changes', JSON.stringify(changes));
                        
                        // Re-render all sections
                        renderIncidents();
                        renderRITMs();
                        renderChanges();
                        updateDashboard();
                        
                        alert('βœ“ All data has been permanently deleted.');
                    } else {
                        alert('Deletion cancelled. You did not type DELETE correctly.');
                    }
                }
            }
        }

        // Format Date
        function formatDate(dateString) {
            const date = new Date(dateString);
            return date.toLocaleString('en-US', {
                year: 'numeric',
                month: 'short',
                day: 'numeric',
                hour: '2-digit',
                minute: '2-digit'
            });
        }

        // Initialize on load
        window.onload = init;
    </script>
</body>
</html>
image

Step 5 β€” Allow HTTP Port 80 in OS Firewall

systemctl start firewalld

Then add port 80:

firewall-cmd --permanent --add-port=80/tcp
firewall-cmd --reload

Verify:

firewall-cmd --list-ports
image

Step 6 β€” Verify Port 80

ss -lntp | grep :80

Port 80 should be in the LISTEN state.

image

Step 7 β€” Verify the Website Locally

curl http://localhost (pvt ip of instance)

Expected output:

Website content

This confirms that the web server and website are working locally.

Step 8: Configure Security List

Add an inbound rule to the Security List associated with the Bastion Server's subnet:

Source : 0.0.0.0/0

Protocol : TCP

Destination Port : 80

Description : Allow HTTP traffic from the Internet

image

Step 9: Verify Route Table

Ensure that the Route Table associated with the Bastion Server's subnet has a default route to the Internet Gateway:

Destination : 0.0.0.0/0

Target Type : Internet Gateway

image

Step 10: Final Website Verification

Open a web browser and enter the Bastion Server's Public IP:

http://

image

Phase 2: Step-by-step guide to Host Application in Private Server

Server Setup

Step 1 β€” Connect to the Server

Connect to the server using SSH/Bastion.

Step 2 β€” Install Apache Web Server

yum install httpd

Step 3 β€” Start and Enable Apache

systemctl start httpd

systemctl enable httpd

Verify:

systemctl status httpd

The status should show:

Active: active (running)

image image image image

Step 4 β€” Create the Website

cd /var/www/html
vi index.html

Add:

:wq! β†’ Save and exit.

Step 5 β€” Allow HTTP Port 80 in OS Firewall

Start firewall service

firewall-cmd --permanent --add-port=80/tcp
firewall-cmd --reload

Verify:

firewall-cmd --list-ports

http should be listed.

image

Step 6 β€” Verify Port 80

ss -lntp | grep :80
image

Step 7 β€” Verify the Website Locally

curl http://localhost

Expected output:

Website content

This confirms that the web server and website are working locally.

image

Note: We are currently configuring only one server. Repeat the same steps on the second server as well to ensure that both servers have the same web server, website configuration, port 80, and OS firewall settings

Load Balancer Setup

Step 1: Add Details

  • Enter a suitable name for the Load Balancer.

2.Visibility Type:

  • Select Public.

3.Assign a Public IP Address:

  • Select Ephemeral or Reserved, based on the requirement.

  • For a fixed public IP, select Reserved.

4.Choose Networking:

  • Select the appropriate Compartment.

  • Select the required VCN.

  • Select the Web Subnet where the Load Balancer will be deployed.

5.Network Security Groups:

Select the appropriate NSG if NSGs are being used.

Otherwise, leave this option unconfigured.

6.Bandwidth:

  • Select the required Minimum Bandwidth.

  • Select the required Maximum Bandwidth according to expected traffic.

7.Security:

  • Enable Web Application Firewall (WAF) if application-layer protection is required.

  • Otherwise, leave it disabled.

8.Acceleration:

  • Enable Acceleration if required for the application.

Click Next.

image image image

Step 2: Choose Backends

Load Balancing Policy

Select the appropriate policy based on the application requirement:

  1. Weighted Round Robin β€” distributes traffic according to assigned weights.

  2. IP Hash β€” directs requests from the same client to the same backend.

  3. Least Connections β€” sends traffic to the backend with the fewest active connections.

For a standard web application, Weighted Round Robin can be selected.

Select Backend Servers

Select the Backend Compartment.

Select the required backend instances.

Add the required backend servers to the backend set.

Verify that the selected backend servers are added successfully.

Specify Health Check Policy

Configure the health check for the backend servers:

Protocol: Select TCP

Port: 80

Interval: Configure the required interval.

Timeout: Configure the required timeout.

Number of Retries: Configure the required retry count.

Backend Set

Enter a suitable Backend Set Name. Max Connections

Enable and configure this option if the application requires a maximum connection limit. Otherwise, leave it disabled. Security List

Select the appropriate option:

Manually configure security list rules, or

Automatically add security list rules For controlled environments, manually configuring the required rules provides better control.

Session Persistence

Select the appropriate option:

Disable session persistence when requests can be handled by any backend.

Enable cookie-based persistence when the application requires client sessions to remain on the same backend.

Click Next.

image image

Step 3: Configure Listener Configure the listener:

1.Listener Name

Enter a suitable listener name. 2.Traffic Type

Select HTTP. 3.Port

Enter 80. SSL/TLS

If HTTPS is required:

Select HTTPS as the traffic type.

Configure the required SSL certificate and TLS settings.

If HTTP is being used:

Keep SSL/TLS configuration disabled. Click Next.

image

Step 4: Manage Logging Error Logs

Enable Error Logging.

Select an existing Log Group or create a new Log Group.

Enter a suitable Log Name.

β€’Configure the required log retention period.

Access Logs

Enable Access Logging.

Select an existing Log Group or create a new Log Group.

Enter a suitable Log Name.

Configure the required log retention period.

Request ID

Enable Request ID.

Configure the request ID header name if required.

Click Next.

image image

Step 5: Review and Create Review the Load Balancer configuration, including:

Load Balancer details

Web Subnet

Backend Set and Backend Servers

Health Check

Listener

Logging configuration

If all configurations are correct, click Submit / Create Load Balancer.

image

Step 6: Verify Load Balancer After the Load Balancer is created:

1.Verify the Load Balancer status is Active.

2.Verify the assigned IP address.

3.Open Backend Sets.

4.Verify all required backend servers are listed.

5.Verify the backend port.

6.Check the health status of each backend.

7.Open Listeners and verify the listener status.

8.Verify the listener is listening on the expected port.

image image

Note: The Load Balancer is currently showing a Critical backend health status. Therefore, the required network configuration, including Security Lists and Route Tables, must be completed to allow traffic between the Load Balancer and the backend servers.

OCI Network Configuration

After creating the Load Balancer, configure the Security Lists and Route Tables associated with the Web, App, and DB subnets.

Step 1: Configure Web Subnet

Subnet: Web Subnet (10.0.1.128/27)

Purpose: Hosts the public Load Balancer.

1.1 Configure Web Security List β€” Ingress

Add the following rule:

image image

1.2 Configure Web Security List β€” Egress

image image

1.3 Configure Web Route Table

Add:

image image

Step 2: Configure App Subnet

Subnet: App Subnet (10.0.1.0/26)

Purpose: Hosts the backend application/web servers.

2.1 Configure App Security List β€” Ingress

Add:

image image

If SSH administration is required, also add:

image

2.2 Configure App Security List β€” Egress

Add:

image image

2.3 Configure App Route Table

If the application servers require outbound Internet access,

add:

image image

Step 3: Configure DB Subnet

Subnet: DB Subnet (10.0.1.96/27)

Purpose: Hosts the database server.

3.1 Configure DB Security List β€” Ingress

Add:

image image

3.2 Configure DB Security List β€” Egress

image image

3.3 Configure DB Route Table

Do not add an Internet Gateway or NAT Gateway default route if the database does not require Internet access.

The VCN's local route will allow communication between the App and DB subnets.

Verify Load Balancer Connectivity

After completing the Load Balancer and network configuration, verify that the application is accessible through the Load Balancer.

1.Open the Load Balancer in the OCI Console.

2.Copy the Public IP Address assigned to the Load Balancer.

3.Open a web browser and enter the Load Balancer Public IP using HTTP:

http://<Load-Balancer-Public-IP>

4.Verify that the application website is displayed successfully.

5.Refresh the page multiple times to verify that traffic is being distributed across the configured backend servers.

6.Confirm that the backend servers are showing Healthy status in the Load Balancer backend set.

Expected Result:

The application website should be accessible through the Load Balancer Public IP, confirming that the Load Balancer, networking, listener, and backend connectivity are working correctly.

image image
⚠️ **GitHub.com Fallback** ⚠️