mirror of
https://github.com/stefanpejcic/openpanel
synced 2025-06-26 18:28:26 +00:00
278 lines
19 KiB
HTML
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 %}
|
|
|
|
<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 %}
|
|
|