Index: /branches/rel_apv_10_7/usr/click/webui/htdocs/new/src/apv/templates/monitoring/prometheus/index/basic.html
===================================================================
--- /branches/rel_apv_10_7/usr/click/webui/htdocs/new/src/apv/templates/monitoring/prometheus/index/basic.html	(revision 40064)
+++ /branches/rel_apv_10_7/usr/click/webui/htdocs/new/src/apv/templates/monitoring/prometheus/index/basic.html	(working copy)
@@ -245,7 +245,7 @@
                                                     <div class="file-upload">
                                                         <input type="text" id="private-key-url" name="private-key" class="form-control" />
                                                     </div>
-        
+
                                                     <label class="nested-name">Certificate URL</label>
                                                     <div class="file-upload">
                                                         <input type="text" id="certificate-url" name="certificate" class="form-control" />
@@ -312,7 +312,7 @@
                                                     <div class="file-upload">
                                                         <textarea id="private-key-textarea-manual" class="file-text"></textarea>
                                                     </div>
-        
+
                                                     <label class="nested-name">Certificate</label>
                                                     <div class="file-upload">
                                                         <textarea id="certificate-textarea-manual" class="file-text"></textarea>
@@ -398,6 +398,31 @@
         </div>
     </div>
 </div>
+
+<!-- Certificate Alert Modal -->
+<div id="ALERT_CERT" class="modal modal-alert warning" tabindex="-1" data-width="460" style="display: none; padding-right: 15px;">
+    <div class="modal-dialog">
+        <div class="modal-content">
+            <div class="modal-header">
+                <h6><i class="fa fa-warning"></i>&nbsp;&nbsp;Please correct these errors</h6>
+            </div>
+            <div class="modal-body">
+                <b>Certificate Required</b> -- Please set the private key and certificate first.
+            </div>
+            <div class="modal-footer">
+                <button type="button" data-dismiss="modal" class="btn btn-default cert-alert-ok">OK</button>
+            </div>
+        </div>
+    </div>
+</div>
+
+<!-- Loading Modal -->
+<div id="LOADING_MODAL">
+    <div class="loading-content">
+        <div class="loading-spinner"></div>
+    </div>
+</div>
+
 <style type="text/css">
     #frame_model_main .main-form {
         padding-bottom: 50px;
@@ -507,6 +532,46 @@
         flex-direction: column;
         gap: 8px;
     }
+    #ALERT_CERT {
+        background-color: rgba(0, 0, 0, 0.5);
+        z-index: 10000 !important;
+    }
+    /* Loading Modal Styles */
+    #LOADING_MODAL {
+        position: fixed;
+        top: 0;
+        left: 0;
+        width: 100%;
+        height: 100%;
+        background: rgba(0, 0, 0, 0.5);
+        z-index: 9999;
+        display: none;
+        justify-content: center;
+        align-items: center;
+    }
+
+    #LOADING_MODAL .loading-content {
+        background: transparent;
+        padding: 0;
+        border-radius: 0;
+        text-align: center;
+        min-width: auto;
+    }
+
+    #LOADING_MODAL .loading-spinner {
+        border: 4px solid rgba(255, 255, 255, 0.3);
+        border-top: 4px solid #fff;
+        border-radius: 50%;
+        width: 40px;
+        height: 40px;
+        animation: spin 1s linear infinite;
+        margin: 0 auto;
+    }
+
+    @keyframes spin {
+        0% { transform: rotate(0deg); }
+        100% { transform: rotate(360deg); }
+    }
 </style>
 <script type="text/javascript">
 require(['list-list'],function(){
@@ -601,20 +666,32 @@
 
         $('.select-level').selectpicker();
 
-        function getBasicSetting() {
+        function getBasicSetting(skipLoading = false) {
             const cachedData = localStorage.getItem('basicSettingData');
             if (cachedData) {
                 const data = JSON.parse(cachedData);
                 updateFormStateByData(data);
-                return;
-            }
-            return $.ajax({
+            } else {
+                // Show loading when fetching data from server (unless skipped)
+                if (!skipLoading) {
+                    showLoading();
+                }
+
+                return $.ajax({
                 url: '/api/apv/prometheus/BasicSetting/_field_group?name=get',
                 type: 'GET',
                 cache: false,
                 dataType: 'json'
             }).then(function(data){
+                if (!skipLoading) {
+                    hideLoading();
+                }
                 updateFormStateByData(data);
+            }).catch(function(error) {
+                if (!skipLoading) {
+                    hideLoading();
+                }
+                console.error('Failed to get basic settings:', error);
             });
         }
 
@@ -632,6 +709,33 @@
             isInitializing = false;
         }
 
+        function checkCertificateStatus() {
+            return $.ajax({
+                url: '/api/apv/prometheus/BasicSetting/_field_group?name=ssl_cert',
+                type: 'GET',
+                cache: false,
+                dataType: 'json'
+            }).then(function(data){
+                const hasPrivateKey = data.private_key && data.private_key.trim() !== '';
+                const hasCertificate = data.cert_content && data.cert_content.trim() !== '';
+
+                if (hasPrivateKey && hasCertificate) {
+                    // Show remove button, hide import button
+                    $('.lightbox-toggle').hide();
+                    $('.remove-data').show();
+                } else {
+                    // Show import button, hide remove button
+                    $('.lightbox-toggle').show();
+                    $('.remove-data').hide();
+                }
+            }).catch(function(error) {
+                console.error('Failed to check certificate status:', error);
+                // Default to showing import button on error
+                $('.lightbox-toggle').show();
+                $('.remove-data').hide();
+            });
+        }
+
         async function updateEnableSetting() {
             try {
                 const params = {
@@ -647,14 +751,32 @@
                     contentType: 'application/x-www-form-urlencoded',
                     dataType: 'json'
                 });
-                updateLocalStorageBasicSetting(params);
-                console.log('Upload successful:', response);
+
+                // Check if response indicates an error (first element is false)
+                if (Array.isArray(response) && response[0] === false) {
+                    // Revert the switch state
+                    formState.enable = false;
+                    enableSwitch.bootstrapSwitch('setState', false, true);
+                    toggleControl();
+                    // Show alert modal with error message
+                    const errorMessage = response[1] || 'Operation failed';
+                    showCertificateAlert(errorMessage);
+                } else {
+                    updateLocalStorageBasicSetting(params);
+                }
             } catch (error) {
                 console.error('API call failed:', error);
+                // Revert the switch state on error
+                formState.enable = false;
+                enableSwitch.bootstrapSwitch('setState', false, true);
+                toggleControl();
             }
         }
 
         async function updateBasicSetting() {
+            // Show loading
+            showLoading();
+
             try {
                 const params = {
                     'port': parseInt(formState.port),
@@ -671,10 +793,27 @@
                     contentType: 'application/x-www-form-urlencoded',
                     dataType: 'json'
                 });
-                updateLocalStorageBasicSetting(params);
-                console.log('Upload successful:', response);
+
+                // Hide loading
+                hideLoading();
+
+                // Check if response indicates an error (first element is false)
+                if (Array.isArray(response) && response[0] === false) {
+                    // Revert HTTPS setting
+                    formState.enable_https = false;
+                    enableHttpsSwitch.bootstrapSwitch('setState', false, true);
+                    // Show alert modal with error message
+                    const errorMessage = response[1] || 'Operation failed';
+                    showCertificateAlert(errorMessage);
+                    return false; // Return false to indicate failure
+                } else {
+                    updateLocalStorageBasicSetting(params);
+                    return true; // Return true to indicate success
+                }
             } catch (error) {
                 console.error('API call failed:', error);
+                hideLoading();
+                return false; // Return false to indicate failure
             }
         }
 
@@ -703,7 +842,6 @@
                     contentType: 'application/x-www-form-urlencoded',
                     dataType: 'json'
                 });
-                console.log('Upload successful:', response);
             } catch (error) {
                 console.error('API call failed:', error);
             }
@@ -720,7 +858,6 @@
                     data: $.param(data),
                     contentType: 'application/x-www-form-urlencoded',
                 });
-                console.log('Upload successful:', response);
             } catch (error) {
                 console.error('API call failed:', error);
             }
@@ -803,7 +940,6 @@
             $('#prometheus_token tbody input[type="checkbox"]:checked').each(function() {
                 selectedTokens.push($(this).val());
             });
-            console.log('Selected Tokens:', selectedTokens);
 
             if (selectedTokens.length > 0) {
                 $('.delete-btn').removeClass('disabled');
@@ -827,12 +963,15 @@
 
         async function initialize() {
             await getBasicSetting();
+            await checkCertificateStatus();
             populateDataTable();
         }
 
         $('.btn-submit').click(async function() {
-            await updateBasicSetting();
-            refreshPage();
+            const success = await updateBasicSetting();
+            if (success) {
+                refreshPage();
+            }
         });
 
         function refreshPage() {
@@ -886,19 +1025,94 @@
             });
         }
 
+        // PEM format validation and cleaning function
+        function validateAndCleanPEM(content, type) {
+            if (!content || typeof content !== 'string') {
+                return null;
+            }
+
+            let cleaned = content.trim();
+
+            // Only process URL encoding if we detect URL-encoded markers
+            const hasUrlEncoding = content.includes('BEGIN+') || content.includes('END+');
+
+            if (hasUrlEncoding) {
+                try {
+                    // Replace + with spaces (URL encoding) only in headers
+                    cleaned = cleaned.replace(/-----BEGIN\+([^-]+)\+-----/g, '-----BEGIN $1-----');
+                    cleaned = cleaned.replace(/-----END\+([^-]+)\+-----/g, '-----END $1-----');
+
+                    // Try to decode URL encoding if needed
+                    if (cleaned !== content) {
+                        cleaned = decodeURIComponent(cleaned);
+                    }
+                } catch (e) {
+                    // If decoding fails, just clean the headers
+                    cleaned = content.replace(/-----BEGIN\+([^-]+)\+-----/g, '-----BEGIN $1-----');
+                    cleaned = cleaned.replace(/-----END\+([^-]+)\+-----/g, '-----END $1-----');
+                }
+            }
+
+            // Normalize line endings
+            cleaned = cleaned.replace(/\r\n/g, '\n').replace(/\r/g, '\n');
+
+            // Validate format
+            if (type === 'key') {
+                // Support both PRIVATE KEY and RSA PRIVATE KEY formats
+                const hasValidStart = cleaned.startsWith('-----BEGIN PRIVATE KEY-----') ||
+                                    cleaned.startsWith('-----BEGIN RSA PRIVATE KEY-----') ||
+                                    cleaned.startsWith('-----BEGIN EC PRIVATE KEY-----');
+                const hasValidEnd = cleaned.includes('-----END PRIVATE KEY-----') ||
+                                   cleaned.includes('-----END RSA PRIVATE KEY-----') ||
+                                   cleaned.includes('-----END EC PRIVATE KEY-----');
+
+                if (hasValidStart && hasValidEnd) {
+                    return cleaned;
+                }
+            } else if (type === 'cert') {
+                const hasValidStart = cleaned.startsWith('-----BEGIN CERTIFICATE-----');
+                const hasValidEnd = cleaned.includes('-----END CERTIFICATE-----');
+
+                if (hasValidStart && hasValidEnd) {
+                    return cleaned;
+                }
+            }
+
+            return null;
+        }
+
         $('#upload-cert-button-file').click(async function() {
             var privateKey = $('#private-key-textarea').val();
             var certificate = $('#certificate-textarea').val();
 
-            if (privateKey && certificate) {
-                await uploadCertFile(privateKey, certificate);
-            } else {
+            if (!privateKey || !certificate) {
                 alert('Please select both private key and certificate files.');
+                return;
+            }
+
+            // Clean and validate PEM format
+            const cleanPrivateKey = validateAndCleanPEM(privateKey, 'key');
+            const cleanCertificate = validateAndCleanPEM(certificate, 'cert');
+
+            if (!cleanPrivateKey) {
+                alert('Private key format is incorrect. Please ensure it is in valid PEM format.\nMake sure it contains complete BEGIN and END markers.');
+                return;
+            }
+
+            if (!cleanCertificate) {
+                alert('Certificate format is incorrect. Please ensure it is in valid PEM format.\nMake sure it contains complete BEGIN and END markers.');
+                return;
             }
+
+            await uploadCertFile(cleanPrivateKey, cleanCertificate);
         });
 
         async function uploadCertFile(privateKeyContent, certificateContent) {
+            // Show loading
+            showLoading();
+
             try {
+                // The content should already be cleaned by the validation function
                 const data = {
                     action: 'Import_Cert',
                     options: JSON.stringify({
@@ -913,6 +1127,7 @@
                         }
                     })
                 };
+
                 const response = await $.ajax({
                     url: '/api/apv/prometheus/BasicSetting/_perform',
                     type: 'POST',
@@ -920,10 +1135,23 @@
                     contentType: 'application/x-www-form-urlencoded',
                     dataType: 'json'
                 });
-                console.log('Upload successful:', response);
-                toggleLightbox();
+
+                // Hide loading
+                hideLoading();
+
+                // Check if response indicates an error (first element is false)
+                if (Array.isArray(response) && response[0] === false) {
+                    const errorMessage = response[1] || 'Certificate upload failed';
+                    showCertificateAlert(errorMessage);
+                } else {
+                    toggleLightbox();
+                    // Refresh certificate status after successful upload
+                    await checkCertificateStatus();
+                }
             } catch (error) {
                 console.error('API call failed:', error);
+                hideLoading();
+                showCertificateAlert('Certificate upload failed due to network error.');
             }
         }
 
@@ -939,8 +1167,11 @@
         });
 
         async function uploadCertManual(privateKeyContent, certificateContent) {
-            try {
+            // Show loading
+            showLoading();
 
+            try {
+                // The content should already be cleaned by the validation function
                 const data = {
                     action: 'Import_Cert',
                     options: JSON.stringify({
@@ -962,10 +1193,23 @@
                     contentType: 'application/x-www-form-urlencoded',
                     dataType: 'json'
                 });
-                console.log('Upload successful:', response);
-                toggleLightbox();
+
+                // Hide loading
+                hideLoading();
+
+                // Check if response indicates an error (first element is false)
+                if (Array.isArray(response) && response[0] === false) {
+                    const errorMessage = response[1] || 'Certificate upload failed';
+                    showCertificateAlert(errorMessage);
+                } else {
+                    toggleLightbox();
+                    // Refresh certificate status after successful upload
+                    await checkCertificateStatus();
+                }
             } catch (error) {
                 console.error('API call failed:', error);
+                hideLoading();
+                showCertificateAlert('Certificate upload failed due to network error.');
             }
         }
 
@@ -981,8 +1225,10 @@
         });
 
         async function uploadCertUrl(privateKeyContent, certificateContent) {
-            try {
+            // Show loading
+            showLoading();
 
+            try {
                 const data = {
                     action: 'Import_Cert',
                     options: JSON.stringify({
@@ -1004,14 +1250,30 @@
                     contentType: 'application/x-www-form-urlencoded',
                     dataType: 'json'
                 });
-                console.log('Upload successful:', response);
-                toggleLightbox();
+
+                // Hide loading
+                hideLoading();
+
+                // Check if response indicates an error (first element is false)
+                if (Array.isArray(response) && response[0] === false) {
+                    const errorMessage = response[1] || 'Certificate upload failed';
+                    showCertificateAlert(errorMessage);
+                } else {
+                    toggleLightbox();
+                    // Refresh certificate status after successful upload
+                    await checkCertificateStatus();
+                }
             } catch (error) {
                 console.error('API call failed:', error);
+                hideLoading();
+                showCertificateAlert('Certificate upload failed due to network error.');
             }
         }
 
         $('.remove-data').click(async function() {
+            // Show loading
+            showLoading();
+
             try {
                 const data = {
                     action: 'Clear_Cert',
@@ -1028,12 +1290,115 @@
                     contentType: 'application/x-www-form-urlencoded',
                     dataType: 'json'
                 });
-                console.log('Remove successful:', response);
+
+                // Check if response indicates an error (first element is false)
+                if (Array.isArray(response) && response[0] === false) {
+                    // Hide loading on error
+                    hideLoading();
+                    const errorMessage = response[1] || 'Certificate removal failed';
+                    showCertificateAlert(errorMessage);
+                } else {
+                    // Update loading message for next phase
+                    showLoading();
+
+                // After successful certificate removal, disable HTTPS and refresh all settings
+                // First disable HTTPS since certificate is removed (skip events to avoid showing Save Changes)
+                formState.enable_https = false;
+                enableHttpsSwitch.bootstrapSwitch('setState', false, true);
+
+                // Update local storage immediately to reflect HTTPS disabled state
+                updateLocalStorageBasicSetting({ enable_https: false });
+
+                // Update the HTTPS setting on server
+                try {
+                    const httpsParams = {
+                        'enable_https': false
+                    };
+                    const httpsData = {
+                        post_data: JSON.stringify(httpsParams)
+                    };
+                    await $.ajax({
+                        url: '/api/apv/prometheus/BasicSetting/_update',
+                        type: 'POST',
+                        data: $.param(httpsData),
+                        contentType: 'application/x-www-form-urlencoded',
+                        dataType: 'json'
+                    });
+                } catch (httpsError) {
+                    console.error('Failed to disable HTTPS after certificate removal:', httpsError);
+                }
+
+                // Refresh certificate status and get latest settings
+                await checkCertificateStatus();
+                // Re-get basic setting data to ensure all states are synchronized
+                await getBasicSetting(true);
+
+                // Clear any cached data to force fresh state
+                localStorage.removeItem('basicSettingData');
+
+                // Hide loading after all operations complete
+                hideLoading();
             } catch (error) {
                 console.error('API call failed:', error);
+                hideLoading();
+                showCertificateAlert('Certificate removal failed due to network error.');
             }
         });
 
+        // Function to show loading modal
+        function showLoading() {
+            $('#LOADING_MODAL').css('display', 'flex');
+        }
+
+        // Function to hide loading modal
+        function hideLoading() {
+            $('#LOADING_MODAL').hide();
+        }
+
+        // Function to show certificate alert modal
+        function showCertificateAlert(errorMessage) {
+            // Update modal content with the error message
+            const message = errorMessage || 'Please set the private key and certificate first.';
+            $('#ALERT_CERT .modal-body').html('<b>Error</b> -- ' + message.replace(/↵/g, '<br>'));
+
+            // Force show the modal
+            $('#ALERT_CERT').removeClass('in').show().addClass('in');
+        }
+
+        // Handle certificate alert modal close
+        $('.cert-alert-ok, [data-dismiss="modal"]').click(function() {
+            $('#ALERT_CERT').removeClass('in').hide();
+        });
+
+        // Close modal when clicking outside
+        $('#ALERT_CERT').click(function(e) {
+            if (e.target === this) {
+                $(this).removeClass('in').hide();
+            }
+        });
+
+        // Handle tab switching for nested-radio elements
+        $('.nested-radio').click(function () {
+            $(this).addClass('active');
+            $(this).nextAll().removeClass('active');
+            $(this).prevAll().removeClass('active');
+
+            // Handle tab content switching
+            var selectedValue = $(this).find('input[type="radio"]').val();
+            $('.form-nested').hide();
+            if (selectedValue === 'local') {
+                $('.group_local').show();
+            } else if (selectedValue === 'online') {
+                $('.group_online').show();
+            } else if (selectedValue === 'manual_input') {
+                $('.group_manual_input').show();
+            }
+        });
+
+        // Initialize with first tab active
+        $('.form-nested').hide();
+        $('.group_local').show();
+
     });
 })
 </script>
\ No newline at end of file
