Files
openpanel/templates/tabler/services/status.html
2024-10-25 01:44:26 +02:00

278 lines
19 KiB
HTML

{% extends 'base.html' %}
{% block content %}
<!-- Page header -->
<div class="page-header mt-0 d-print-none">
<div class="container-xl">
<div class="row g-2 align-items-center">
<div class="col">
<!-- Page pre-title -->
<div class="page-pretitle">
Services
</div>
<h2 class="page-title">
Status
</h2>
</div>
<!-- Page title actions -->
<div class="col-auto ms-auto mt-0 d-print-none">
<div class="btn-list">
</div>
</div>
</div>
</div>
</div>
<!-- Page body -->
<div class="page-body">
{% if statuses.items() %}
<div class="">
<div class="row row-deck row-cards">
<div class="col-lg-12" id="statuses">
<div class="card">
<div class="table-responsive">
<table class="table card-table table-vcenter text-nowrap datatable">
<thead>
<tr>
<th class="w-1">Status</th>
<th>Service Name</th>
<th>Version</th>
<th>Port</th>
<th class="w-1">Actions</th>
</tr>
</thead>
<tbody>
{% for service_display_name, details in statuses.items() %}
<tr>
<td>
{% if details.status == True %}
<img title="Running" data-bs-toggle="tooltip" data-bs-placement="right" src="data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+Cjxzdmcgd2lkdGg9IjI2cHgiIGhlaWdodD0iMjZweCIgdmlld0JveD0iMCAwIDI2IDI2IiB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiPgogICAgPCEtLSBHZW5lcmF0b3I6IFNrZXRjaCA0My4yICgzOTA2OSkgLSBodHRwOi8vd3d3LmJvaGVtaWFuY29kaW5nLmNvbS9za2V0Y2ggLS0+CiAgICA8dGl0bGU+Y2hlY2s8L3RpdGxlPgogICAgPGRlc2M+Q3JlYXRlZCB3aXRoIFNrZXRjaC48L2Rlc2M+CiAgICA8ZGVmcz48L2RlZnM+CiAgICA8ZyBpZD0iUGFnZS0xIiBzdHJva2U9Im5vbmUiIHN0cm9rZS13aWR0aD0iMSIgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJldmVub2RkIj4KICAgICAgICA8ZyBpZD0iY2hlY2siPgogICAgICAgICAgICA8Y2lyY2xlIGlkPSJPdmFsIiBzdHJva2U9IiM1MUJEMjIiIHN0cm9rZS13aWR0aD0iMiIgY3g9IjEzIiBjeT0iMTMiIHI9IjEyIj48L2NpcmNsZT4KICAgICAgICAgICAgPHBhdGggZD0iTTEyLjIyNjc5OTgsMTUuOTA4MjgzMSBMOS41ODQwNjI4MywxMi44ODkxMDAzIEM5LjA2NTc3NDY1LDEyLjI5Njk4NDMgOC4xNzQ2NTQ0NCwxMi4yMzI4NTM0IDcuNTgwODExNTMsMTIuNzMxMTQ2NyBMNy42NzcwNzI0MywxMi42NTAzNzQyIEM3LjA3ODI3MDcyLDEzLjE1MjgyODUgNy4wMTczMDYxLDE0LjAyNzI3OSA3LjUyOTgyODM0LDE0LjYxMjgwNzcgTDExLjM3MDgzODMsMTkuMDAwOTUyIEMxMS40NzQ0NDk2LDE5LjExOTMyMjQgMTEuNTkyOTYxLDE5LjIxNjU5MjEgMTEuNzIxMDE4NiwxOS4yOTI0ODYyIEMxMS42ODQ3MTYzLDE5LjI3NDM0MDcgMTEuNjQ4ODM4LDE5LjI1NDY1MTIgMTEuNjEzNDY0NiwxOS4yMzMzOTY3IEwxMS44MzcyNzcsMTkuMzY3ODc2OCBDMTEuODAxNzMzOSwxOS4zNDY1MjAzIDExLjc2NzQxMjcsMTkuMzIzOTU5MiAxMS43MzQzMzI1LDE5LjMwMDI4IEMxMi4yNDUwMTU1LDE5LjU5NTUzNzYgMTIuOTAzMDY1OSwxOS41NTQxNDEzIDEzLjM3NDA4OTYsMTkuMTU4OTA1NiBMMTMuMjc3ODI4NywxOS4yMzk2NzgxIEMxMy4zMzk4MjEzLDE5LjE4NzY2MDEgMTMuMzk2MDQ5NCwxOS4xMzE2NTUgMTMuNDQ2NDczNCwxOS4wNzIzOTYzIEMxMy41NTU0NTQ5LDE4Ljk3NzE1MTIgMTMuNjUyNTI0OCwxOC44NjUxNTg3IDEzLjczMzUyNjMsMTguNzM3NDg3NSBMMTkuMzE2NTA0NCw5LjkzNzgyODc1IEMxOS43NjIwMzksOS4yMzU1OTU1NCAxOS41NDQyMTYsOC4zMDQ4ODA5MSAxOC44MjIzMDg4LDcuODcxMTE1MjkgTDE5LjA0NjEyMTIsOC4wMDU1OTUzNSBDMTguMzE3NjUyNiw3LjU2Nzg4NzI1IDE3LjM3NTAzLDcuNzkzODU1ODYgMTYuOTI2MDU5NSw4LjUwMTUwNDU3IEwxMi4yMjY3OTk4LDE1LjkwODI4MzEgWiIgaWQ9IkNvbWJpbmVkLVNoYXBlIiBmaWxsPSIjNTFCRDIyIj48L3BhdGg+CiAgICAgICAgPC9nPgogICAgPC9nPgo8L3N2Zz4=">
{% elif details.status == False %}
<img title="Stopped" data-bs-toggle="tooltip" data-bs-placement="right" src="data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+Cjxzdmcgd2lkdGg9IjI2cHgiIGhlaWdodD0iMjZweCIgdmlld0JveD0iMCAwIDI2IDI2IiB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiPgogICAgPCEtLSBHZW5lcmF0b3I6IFNrZXRjaCA0My4yICgzOTA2OSkgLSBodHRwOi8vd3d3LmJvaGVtaWFuY29kaW5nLmNvbS9za2V0Y2ggLS0+CiAgICA8dGl0bGU+ZGFuZ2VyPC90aXRsZT4KICAgIDxkZXNjPkNyZWF0ZWQgd2l0aCBTa2V0Y2guPC9kZXNjPgogICAgPGRlZnM+PC9kZWZzPgogICAgPGcgaWQ9IlBhZ2UtMSIgc3Ryb2tlPSJub25lIiBzdHJva2Utd2lkdGg9IjEiIGZpbGw9Im5vbmUiIGZpbGwtcnVsZT0iZXZlbm9kZCI+CiAgICAgICAgPGcgaWQ9ImRhbmdlciI+CiAgICAgICAgICAgIDxjaXJjbGUgaWQ9Ik92YWwiIHN0cm9rZT0iI0UxNTY0QiIgc3Ryb2tlLXdpZHRoPSIyIiBjeD0iMTMiIGN5PSIxMyIgcj0iMTIiPjwvY2lyY2xlPgogICAgICAgICAgICA8cGF0aCBkPSJNMTMsMTAuOTAwMjE2NSBMOS44MjQ1NDI1NSw3LjcyNDc1OSBDOS40MzU1ODMwMiw3LjMzNTc5OTQ4IDguODA1MDY4MTksNy4zMzc3ODg5NSA4LjQxNDk1MTM5LDcuNzI3OTA1NzYgTDcuNzA2MzY4OTYsOC40MzY0ODgxOCBDNy4zMTMwMDgxMiw4LjgyOTg0OTAyIDcuMzE0ODQzMyw5LjQ1NzcwMDQ1IDcuNzAzMjIyMiw5Ljg0NjA3OTM1IEwxMC44Nzg2Nzk3LDEzLjAyMTUzNjggTDcuODAzNzI3MjcsMTYuMDk2NDg5MiBDNy40MTUzNDgzNywxNi40ODQ4NjgxIDcuNDEzNTEzMTksMTcuMTEyNzE5NSA3LjgwNjg3NDAyLDE3LjUwNjA4MDQgTDguNTE1NDU2NDUsMTguMjE0NjYyOCBDOC45MDU1NzMyNSwxOC42MDQ3Nzk2IDkuNTM2MDg4MDksMTguNjA2NzY5MSA5LjkyNTA0NzYxLDE4LjIxNzgwOTUgTDEzLDE1LjE0Mjg1NzEgTDE2LjA3NDk1MjQsMTguMjE3ODA5NSBDMTYuNDYzOTExOSwxOC42MDY3NjkxIDE3LjA5NDQyNjcsMTguNjA0Nzc5NiAxNy40ODQ1NDM2LDE4LjIxNDY2MjggTDE4LjE5MzEyNiwxNy41MDYwODA0IEMxOC41ODY0ODY4LDE3LjExMjcxOTUgMTguNTg0NjUxNiwxNi40ODQ4NjgxIDE4LjE5NjI3MjcsMTYuMDk2NDg5MiBMMTUuMTIxMzIwMywxMy4wMjE1MzY4IEwxOC4yOTY3Nzc4LDkuODQ2MDc5MzUgQzE4LjY4NTE1NjcsOS40NTc3MDA0NSAxOC42ODY5OTE5LDguODI5ODQ5MDIgMTguMjkzNjMxLDguNDM2NDg4MTggTDE3LjU4NTA0ODYsNy43Mjc5MDU3NiBDMTcuMTk0OTMxOCw3LjMzNzc4ODk1IDE2LjU2NDQxNyw3LjMzNTc5OTQ4IDE2LjE3NTQ1NzUsNy43MjQ3NTkgTDEzLDEwLjkwMDIxNjUgWiIgaWQ9IkNvbWJpbmVkLVNoYXBlIiBmaWxsPSIjRTE1NjRCIj48L3BhdGg+CiAgICAgICAgPC9nPgogICAgPC9nPgo8L3N2Zz4=">
{% elif details.status is none %}
<span class="form-help" data-bs-toggle="popover" data-bs-placement="top" data-bs-content="<p class='mb-2'>This service will start automatically when needed.</p><a href='https://openpanel.com/docs/articles/dev-experience/autostart-services#auto-start-services-in-openadmin' target='_blank'>More information</a><p class='mb-2'></p>" data-bs-html="true">?</span>
{% endif %}
</td>
<td>{{ service_display_name }}<br>
<span class="text-secondary">
({% if details.type == ("docker") %}
<svg height="auto" title="Service is running inside a docker container" data-bs-toggle="tooltip" data-bs-placement="bottom" preserveAspectRatio="xMidYMid" viewBox="0 0 256 185" width="24" xmlns="http://www.w3.org/2000/svg"><path d="m250.715745 70.4971666c-5.764643-3.9998389-18.975281-5.4997786-29.303599-3.4998595-1.200967-9.9995979-6.725416-18.7492462-16.333153-26.4989347l-5.524449-3.9998393-3.843095 5.7497689c-4.803869 7.4996986-7.205803 17.9992766-6.485223 27.9988749.240194 3.4998595 1.441161 9.7496078 5.044063 15.2493871-3.362709 1.9999198-10.328318 4.499819-19.455668 4.499819h-173.65985102l-.48038688 1.9999198c-1.68135403 9.9995972-1.68135403 41.2483422 18.0145073 65.2473782 14.8919927 18.249266 36.9897884 27.498894 66.0531938 27.498894 62.9306788 0 109.5282048-30.248784 131.3858068-84.9965841 8.646963.2499904 27.141857 0 36.509401-18.7492459.240194-.4999801.72058-1.4999404 2.401935-5.2497896l.960773-1.999919zm-110.729172-70.4971666h-26.421278v24.9989952h26.421278zm0 29.9987943h-26.421278v24.9989953h26.421278zm-31.225146 0h-26.4212775v24.9989953h26.4212775zm-31.2251456 0h-26.4212777v24.9989953h26.4212777zm-31.2251464 29.9987943h-26.4212774v24.9989953h26.4212774zm31.2251464 0h-26.4212777v24.9989953h26.4212777zm31.2251456 0h-26.4212775v24.9989953h26.4212775zm31.225146 0h-26.421278v24.9989953h26.421278zm31.225146 0h-26.421278v24.9989953h26.421278z" fill="#2396ed"/></svg> {% elif details.type == ("system") %}{% endif %}{{ details.real_name }})
</span>
</td>
{% if details.version %}
<td><span class="text-secondary">{{ details.version }}</span></td>
{% else %}
<td></td>
{% endif %}
<td>
{% if details.port %}
{% set ports = details.port %}
{% if ports is string %}
<!-- Handle single port as a string -->
<span class="text-secondary">{{ ports }}</span>
{% elif ports is iterable and ports|length > 0 %}
{% if ports|length > 1 %}
{% set port_list = ports|join(', ') %}
<span class="text-secondary">{{ port_list }}</span>
{% else %}
<span class="text-secondary">{{ ports[0] }}</span>
{% endif %}
{% else %}
<span class="text-secondary"> </span>
{% endif %}
{% else %}
<span class="text-secondary"> </span>
{% endif %}
</td>
<td>
{% if details.status %}
{% if details.real_name == 'admin' %}
<button class="btn btn-outline-danger disabled" style="pointer-events: auto;" title="To disable admin panel use Settings > OpenAdmin" data-bs-toggle="tooltip" data-bs-placement="left"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="currentColor" class="icon icon-tabler icons-tabler-filled icon-tabler-player-stop"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M17 4h-10a3 3 0 0 0 -3 3v10a3 3 0 0 0 3 3h10a3 3 0 0 0 3 -3v-10a3 3 0 0 0 -3 -3z" /></svg>Stop</button>
{% else %}
<button
{% if details.real_name == 'docker' %}
title="Stopping Docker service will disrupt user websites" data-bs-toggle="tooltip" data-bs-placement="left"
{% elif details.real_name == 'openpanel' %}
title="Stopping OpenPanel will disable the interface for users, however all their services remain active" data-bs-toggle="tooltip" data-bs-placement="left"
{% elif details.real_name == 'nginx' %}
title="Stopping Nginx will terminate active connections to websites" data-bs-toggle="tooltip" data-bs-placement="left"
{% elif details.real_name == 'openadmin_mailserver' %}
title="Stopping MailServer will disable all email traffic immediately" data-bs-toggle="tooltip" data-bs-placement="left"
{% elif details.real_name == 'openadmin_roundcube' %}
title="Stopping Roundcube will disable webmail access for all users" data-bs-toggle="tooltip" data-bs-placement="left"
{% elif details.real_name == 'openpanel_mysql' %}
title="Stopping Database will permit all users from login to OpenPanel" data-bs-toggle="tooltip" data-bs-placement="left"
{% elif details.real_name == 'openadmin_ftp' %}
title="Stopping FTP will terminate all active ftp connections" data-bs-toggle="tooltip" data-bs-placement="left"
{% elif details.real_name == 'openpanel_dns' %}
title="Stopping DNS will break resolving dns records for domains" data-bs-toggle="tooltip" data-bs-placement="left"
{% elif details.real_name == 'certbot' %}
title="Stopping Certbot will not stop new SSL generation and renewals" data-bs-toggle="tooltip" data-bs-placement="left"
{% elif details.real_name == 'csf' %}
title="Stopping ConfigServer Firewall will expose your server to everyone on all ports" data-bs-toggle="tooltip" data-bs-placement="left"
{% elif details.real_name == 'ufw' %}
title="Stopping UncomplicatedFirewall will expose your server to everyone on all ports" data-bs-toggle="tooltip" data-bs-placement="left"
{% endif %}
class="btn btn-outline-danger" onclick="controlService('{{ details.real_name }}', 'stop', this, '{{ service_display_name }}', '{{ details.type }}')"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="currentColor" class="icon icon-tabler icons-tabler-filled icon-tabler-player-stop"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M17 4h-10a3 3 0 0 0 -3 3v10a3 3 0 0 0 3 3h10a3 3 0 0 0 3 -3v-10a3 3 0 0 0 -3 -3z" /></svg>Stop</button>
{% endif %}
{% else %}
<button class="btn btn-outline-success" onclick="controlService('{{ details.real_name }}', 'start', this, '{{ service_display_name }}', '{{ details.type }}')"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="currentColor" class="icon icon-tabler icons-tabler-filled icon-tabler-player-play"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M6 4v16a1 1 0 0 0 1.524 .852l13 -8a1 1 0 0 0 0 -1.704l-13 -8a1 1 0 0 0 -1.524 .852z" /></svg>Start</button>
{% endif %}
&nbsp;
<button class="btn btn-outline-warning" onclick="controlService('{{ details.real_name }}', 'restart', this, '{{ service_display_name }}', '{{ details.type }}')"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon icon-tabler icons-tabler-outline icon-tabler-refresh"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M20 11a8.1 8.1 0 0 0 -15.5 -2m-.5 -4v4h4"></path><path d="M4 13a8.1 8.1 0 0 0 15.5 2m.5 4v-4h-4"></path></svg>Restart</button>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div></div>
</div>
</div>
<script>
async function controlService(serviceName, action, button, displayName, serviceType) {
// Disable the button and update its text
button.disabled = true;
button.innerHTML = 'Running...';
// Create a new toast element for each notification
const toastElement = document.createElement('div');
toastElement.className = 'toast';
toastElement.role = 'alert';
toastElement.ariaLive = 'assertive';
toastElement.ariaAtomic = 'true';
toastElement.setAttribute('data-bs-autohide', 'false');
// Create the header
const toastHeader = document.createElement('div');
toastHeader.className = 'toast-header bg-dark text-light';
const strong = document.createElement('strong');
strong.className = 'me-auto';
strong.innerText = `${action.charAt(0).toUpperCase() + action.slice(1)}ing ${displayName}`;
const small = document.createElement('small');
small.innerText = new Date().toLocaleTimeString();
const closeButton = document.createElement('button');
closeButton.type = 'button';
closeButton.className = 'ms-2 btn-close';
closeButton.setAttribute('data-bs-dismiss', 'toast');
closeButton.setAttribute('aria-label', 'Close');
// Append elements to the header
toastHeader.appendChild(strong);
toastHeader.appendChild(small);
toastHeader.appendChild(closeButton);
// Create the body
const toastBody = document.createElement('div');
toastBody.className = 'toast-body bg-light text-dark';
toastBody.innerText = 'Processing your request... Please wait.';
// Append header and body to the toast
toastElement.appendChild(toastHeader);
toastElement.appendChild(toastBody);
// Append the toast to the toast container
document.querySelector('.toast-container').appendChild(toastElement);
// Show the toast
const toast = new bootstrap.Toast(toastElement);
toast.show();
try {
// Make the fetch request to control the service
const response = await fetch('/services/control', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ real_name: serviceName, action: action }),
});
// Parse the JSON response
const result = await response.json();
// Check the result status
if (result.status === 'success') {
// Show success message
toastBody.innerHTML = `${displayName} action completed successfully!<br>Page will refresh to display update information.`;
toast.show();
location.reload();
} else {
// Show error message
toastBody.innerText = 'Error: ' + result.message;
toast.show();
}
} catch (error) {
// Handle any network errors
console.error('Error:', error);
toastBody.innerHTML = 'An error occurred processing the request. Error message:<br><pre>' + error + '</pre>';
toast.show();
} finally {
// Re-enable the button and revert text in case of success or error
button.disabled = false;
// Define action to icon mapping
const actionIcons = {
start: `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="currentColor" class="icon icon-tabler icons-tabler-filled icon-tabler-player-play"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M6 4v16a1 1 0 0 0 1.524 .852l13 -8a1 1 0 0 0 0 -1.704l-13 -8a1 1 0 0 0 -1.524 .852z" /></svg>Start`,
stop: `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="currentColor" class="icon icon-tabler icons-tabler-filled icon-tabler-player-stop"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M17 4h-10a3 3 0 0 0 -3 3v10a3 3 0 0 0 3 3h10a3 3 0 0 0 3 -3v-10a3 3 0 0 0 -3 -3z" /></svg>Stop`,
restart: `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon icon-tabler icons-tabler-outline icon-tabler-refresh"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M20 11a8.1 8.1 0 0 0 -15.5 -2m-.5 -4v4h4"></path><path d="M4 13a8.1 8.1 0 0 0 15.5 2m.5 4v-4h-4"></path></svg>Restart`
};
// Set the button's inner HTML with the corresponding icon
button.innerHTML = actionIcons[action];
}
}
</script>
<div class="toast-container position-fixed bottom-0 end-0 p-2">
</div>
{% else %}
<div class="page page-center">
<div class="container-tight py-4">
<div class="empty">
<div class="empty-header">No Services</div>
<p class="empty-title">Make sure that file <code>/etc/openpanel/openadmin/config/services.json</code> has valid JSON.</p>
<div class="empty-action">
<a href="https://openpanel.com/docs/admin/services/status/" target="_blank" class="btn btn-primary">
View Documentation
</a>
</div>
</div>
</div>
</div>
{% endif %}
</div>
<!--script src="{{ url_for('static', filename='pages/services_status.js') }}" defer></script-->
{% endblock %}