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

1898 lines
99 KiB
HTML

<!-- plans.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">
<h2 class="page-title">Plans</h2>
</div>
<div class="col-auto ms-auto d-print-none">
<div class="btn-list">
<span class="d-none d-sm-inline">
<div class="input-icon">
<span class="input-icon-addon">
<svg
xmlns="http://www.w3.org/2000/svg"
class="icon"
width="24"
height="24"
viewBox="0 0 24 24"
stroke-width="2"
stroke="currentColor"
fill="none"
stroke-linecap="round"
stroke-linejoin="round"
>
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
<path d="M10 10m-7 0a7 7 0 1 0 14 0a7 7 0 1 0 -14 0"></path>
<path d="M21 21l-6 -6"></path>
</svg>
</span>
<input
type="text"
id="userSearchInput"
class="form-control"
placeholder="Search plans"
aria-label="Search in plans"
/>
</div>
</span>
<button
id="addPlanButton"
class="btn btn-primary d-sm-inline-block"
type="button"
{%
if
images
%}
data-bs-toggle="modal"
data-bs-target="#modal-report"
{%
else
%}
data-bs-toggle="tooltip"
data-bs-placement="bottom"
aria-label="Please ensure that Docker images are added before creating plans."
data-bs-original-title="Please ensure that Docker images are added before creating plans."
{%
endif
%}
>
<svg
xmlns="http://www.w3.org/2000/svg"
class="icon"
width="24"
height="24"
viewBox="0 0 24 24"
stroke-width="2"
stroke="currentColor"
fill="none"
stroke-linecap="round"
stroke-linejoin="round"
>
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
<path d="M12 5l0 14"></path>
<path d="M5 12l14 0"></path></svg
>New Plan
</button>
</div>
</div>
</div>
</div>
</div>
<div
class="modal modal-blur fade"
id="modal-report"
tabindex="-1"
style="display: none"
aria-hidden="true"
>
<div class="modal-dialog modal-lg modal-dialog-centered" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">New Plan</h5>
<a style="display: none" href="#"
><svg
xmlns="http://www.w3.org/2000/svg"
class="icon icon-tabler icon-tabler-info-square-rounded-filled"
width="24"
height="24"
viewBox="0 0 24 24"
stroke-width="2"
stroke="currentColor"
fill="none"
stroke-linecap="round"
stroke-linejoin="round"
>
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
<path
d="M12 2l.642 .005l.616 .017l.299 .013l.579 .034l.553 .046c4.687 .455 6.65 2.333 7.166 6.906l.03 .29l.046 .553l.041 .727l.006 .15l.017 .617l.005 .642l-.005 .642l-.017 .616l-.013 .299l-.034 .579l-.046 .553c-.455 4.687 -2.333 6.65 -6.906 7.166l-.29 .03l-.553 .046l-.727 .041l-.15 .006l-.617 .017l-.642 .005l-.642 -.005l-.616 -.017l-.299 -.013l-.579 -.034l-.553 -.046c-4.687 -.455 -6.65 -2.333 -7.166 -6.906l-.03 -.29l-.046 -.553l-.041 -.727l-.006 -.15l-.017 -.617l-.004 -.318v-.648l.004 -.318l.017 -.616l.013 -.299l.034 -.579l.046 -.553c.455 -4.687 2.333 -6.65 6.906 -7.166l.29 -.03l.553 -.046l.727 -.041l.15 -.006l.617 -.017c.21 -.003 .424 -.005 .642 -.005zm0 9h-1l-.117 .007a1 1 0 0 0 0 1.986l.117 .007v3l.007 .117a1 1 0 0 0 .876 .876l.117 .007h1l.117 -.007a1 1 0 0 0 .876 -.876l.007 -.117l-.007 -.117a1 1 0 0 0 -.764 -.857l-.112 -.02l-.117 -.006v-3l-.007 -.117a1 1 0 0 0 -.876 -.876l-.117 -.007zm.01 -3l-.127 .007a1 1 0 0 0 0 1.986l.117 .007l.127 -.007a1 1 0 0 0 0 -1.986l-.117 -.007z"
stroke-width="0"
fill="currentColor"
/>
</svg>
</a>
<button
type="button"
class="btn-close"
data-bs-dismiss="modal"
aria-label="Close"
></button>
</div>
<form id="planForm">
<div class="modal-body">
<div class="form-selectgroup-boxes row mb-3">
<div class="col-lg-4">
<label class="form-label"
>Name: <span class="text-danger">*</span></label
>
<div class="row g-2">
<div class="col">
<input
id="name"
class="form-control"
name="name"
placeholder="Enter plan name"
type="text"
required=""
data-validate
value="{{ plan_template_data.get('name', '') }}"
/>
</div>
<div class="col-auto align-self-center">
<span
class="form-help"
data-bs-toggle="popover"
data-bs-placement="top"
data-bs-content="<p class='mb-0'>Plan name is visible to the users.</p>"
data-bs-html="true"
>?</span
>
</div>
</div>
</div>
<!-- col -->
<div class="col-lg-8">
<label class="form-label">Description:</label>
<div class="row g-2">
<div class="col">
<input
id="description"
class="form-control"
name="description"
placeholder="Enter description"
type="textarea"
value="{{ plan_template_data.get('description', '') }}"
/>
</div>
<div class="col-auto align-self-center">
<span
class="form-help"
data-bs-toggle="popover"
data-bs-placement="top"
data-bs-content="<p class='mb-0'>Description is only visible to Admin users.</p>"
data-bs-html="true"
>?</span
>
</div>
</div>
</div>
</div>
<label class="form-label">Docker image</label>
<div class="form-selectgroup-boxes row mb-3">
<div class="col-lg-4 p-1">
<label class="form-selectgroup-item">
<input
type="radio"
name="docker_image"
value="apache"
class="form-selectgroup-input"
checked=""
/>
<span
class="form-selectgroup-label d-flex align-items-center p-3"
>
<span class="me-3">
<span class="form-selectgroup-check"></span>
</span>
<span class="form-selectgroup-label-content">
<span class="form-selectgroup-title strong mb-1"
><span class="me-2"
><img
class="avatar"
src="{{ url_for('static', filename='images/apache.png') }}" /><br></span
>Apache & MySQL</span
>
&nbsp;<span class="form-help" data-bs-toggle="popover" data-bs-placement="top" data-bs-content="<p>Ubuntu docker image with MySQL 8, Apache 2.4 and PHP 8.2 pre-installed.</p><p class='mb-0'><a href='https://github.com/stefanpejcic/OpenPanel/blob/main/docker/apache/Dockerfile' target='_blank'>View Dockerfile</a></p>" data-bs-html="true">?</span>
</span>
</span>
</label>
</div>
{% if license_type == "Enterprise" %}
<div class="col-lg-4 p-1">
<label class="form-selectgroup-item">
<input
type="radio"
name="docker_image"
value="openpanel/apache-mariadb"
class="form-selectgroup-input"
/>
<span
class="form-selectgroup-label d-flex align-items-center p-3"
>
<span class="me-3">
<span class="form-selectgroup-check"></span>
</span>
<span class="form-selectgroup-label-content">
<span class="form-selectgroup-title strong mb-1"
><span class="me-2"
><img
class="avatar"
src="{{ url_for('static', filename='images/apache.png') }}" /><br></span
>Apache & MariaDB</span
>
&nbsp;<span class="form-help" data-bs-toggle="popover" data-bs-placement="top" data-bs-content="<p>Ubuntu docker image with MariaDB 10, Apache 2.4 and PHP 8.2 pre-installed.</p><p class='mb-0'><a href='https://github.com/stefanpejcic/OpenPanel/blob/main/docker/ubuntu_apache_mariadb/Dockerfile' target='_blank'>View Dockerfile</a></p>" data-bs-html="true">?</span>
</span>
</span>
</label>
</div>
{% endif %}
<div class="col-lg-4 p-1">
<label class="form-selectgroup-item">
<input
type="radio"
name="docker_image"
value="nginx"
class="form-selectgroup-input"
/>
<span
class="form-selectgroup-label d-flex align-items-center p-3"
>
<span class="me-3">
<span class="form-selectgroup-check"></span>
</span>
<span class="form-selectgroup-label-content">
<span class="form-selectgroup-title strong mb-1"
><span class="me-2"
><img
class="avatar"
src="{{ url_for('static', filename='images/nginx.png') }}" /><br></span
>Nginx & MySQL
</span
>
&nbsp;<span class="form-help" data-bs-toggle="popover" data-bs-placement="top" data-bs-content="<p>Ubuntu docker image with MySQL 8, Nginx 1.24 and PHP 8.2 pre-installed.</p><p class='mb-0'><a href='https://github.com/stefanpejcic/OpenPanel/blob/main/docker/nginx/Dockerfile' target='_blank'>View Dockerfile</a></p>" data-bs-html="true">?</span>
</span>
</span>
</label>
</div>
{% if license_type == "Enterprise" %}
<div class="col-lg-4 p-1">
<label class="form-selectgroup-item">
<input
type="radio"
name="docker_image"
value="openpanel/nginx-mariadb"
class="form-selectgroup-input"
/>
<span
class="form-selectgroup-label d-flex align-items-center p-3"
>
<span class="me-3">
<span class="form-selectgroup-check"></span>
</span>
<span class="form-selectgroup-label-content">
<span class="form-selectgroup-title strong mb-1"
><span class="me-2"
><img
class="avatar"
src="{{ url_for('static', filename='images/nginx.png') }}" /><br></span
>Nginx & MariaDB</span
>
&nbsp;<span class="form-help" data-bs-toggle="popover" data-bs-placement="top" data-bs-content="<p>Ubuntu docker image with MariaDB 10, Nginx 1.24 and PHP 8.2 pre-installed.</p><p class='mb-0'><a href='https://github.com/stefanpejcic/OpenPanel/blob/main/docker/ubuntu_nginx_mariadb/Dockerfile' target='_blank'>View Dockerfile</a></p>" data-bs-html="true">?</span>
</span>
</span>
</label>
</div>
<div class="col-lg-4 p-1">
<label class="form-selectgroup-item">
<input
type="radio"
name="docker_image"
value="custom"
class="form-selectgroup-input"
/>
<span
class="form-selectgroup-label d-flex align-items-center p-3"
>
<div class="ribbon bg-success" style="min-height: 1rem">
new
</div>
<span class="me-3">
<span class="form-selectgroup-check"></span>
</span>
<span class="form-selectgroup-label-content">
<span class="form-selectgroup-title strong mb-1"
><span class="me-2"
>
</span
>Custom</span
>
<br>
<input id="custom_image_name" class="form-control" name="custom_image_name" placeholder="image name" type="text" value="">
</span>
</span>
</label>
</div>
{% endif %}
<div class="col-lg-4 p-1">
<label class="form-selectgroup-item">
<input
type="radio"
name="docker_image"
value="nginx"
class="form-selectgroup-input"
disabled
/>
<span
class="form-selectgroup-label d-flex align-items-center p-3"
>
<div class="ribbon bg-secondary" style="min-height: 1rem">
coming soon
</div>
<span class="me-3">
<span class="form-selectgroup-check"></span>
</span>
<span class="form-selectgroup-label-content">
<span class="form-selectgroup-title strong mb-1"
><span class="me-2"
><img
class="avatar"
src="{{ url_for('static', filename='images/litespeed.png') }}" /><br></span
>LiteSpeed & MySQL</span
>
</span>
</span>
</label>
</div>
</div>
<hr />
<div class="row">
<div class="col-lg-3">
<div class="mb-3">
<label class="form-label"
>Domains: <span class="text-danger">*</span></label
>
<div class="row g-2">
<div class="col">
<input
id="domains_limit"
class="form-control"
name="domains_limit"
placeholder=""
type="number"
required=""
min="0"
data-validate
value="{{ plan_template_data.get('domains', '') }}"
/>
</div>
<div class="col-auto align-self-center">
<span
class="form-help"
data-bs-toggle="popover"
data-bs-placement="top"
data-bs-content="<p>Total number of domains users are allowed to add on this plan.</p><p class='mb-0'>Subdomains are not included in this limit.</p>"
data-bs-html="true"
>?</span
>
</div>
</div>
</div>
</div>
<div class="col-lg-3">
<div class="mb-3">
<label class="form-label"
>Websites: <span class="text-danger">*</span></label
>
<div class="row g-2">
<div class="col">
<input
id="websites_limit"
class="form-control"
name="websites_limit"
placeholder=""
type="number"
required=""
min="0"
data-validate
value="{{ plan_template_data.get('websites', '') }}"
/>
</div>
<div class="col-auto align-self-center">
<span
class="form-help"
data-bs-toggle="popover"
data-bs-placement="top"
data-bs-content="<p>Total number of websites users are allowed to manage on this plan.</p><p class='mb-0'>This encompasses both WordPress sites managed via the WP Manager interface and Python/NodeJS applications administered through the PM2 interface.</p>"
data-bs-html="true"
>?</span
>
</div>
</div>
</div>
</div>
<div class="col-lg-3">
<div class="mb-3">
<label class="form-label"
>Databases: <span class="text-danger">*</span></label
>
<div class="row g-2">
<div class="col">
<input
id="db_limit"
class="form-control"
name="db_limit"
placeholder=""
type="number"
required=""
min="0"
data-validate
value="{{ plan_template_data.get('databases', '') }}"
/>
</div>
<div class="col-auto align-self-center">
<span
class="form-help"
data-bs-toggle="popover"
data-bs-placement="top"
data-bs-content="<p class='mb-0'>Total number of MySQL databases users are allowed to create on this plan.</p>"
data-bs-html="true"
>?</span
>
</div>
</div>
</div>
</div>
<div class="col-lg-3">
<div class="mb-3">
<label class="form-label"
>Emails: <span class="text-danger">*</span></label
>
<div class="row g-2">
<div class="col">
<input
id="email_limit"
class="form-control"
name="email_limit"
placeholder=""
type="number"
required=""
min="0"
data-validate
value="{{ plan_template_data.get('emails', '') }}"
/>
</div>
<div class="col-auto align-self-center">
<span
class="form-help"
data-bs-toggle="popover"
data-bs-placement="top"
data-bs-content="<p class='mb-0'>Total number of Email addresses that can be created by user on this plan.</p>"
data-bs-html="true"
>?</span
>
</div>
</div>
</div>
</div>
<div class="col-lg-3">
<div class="mb-3">
<label class="form-label"
>FTP: <span class="text-danger">*</span></label
>
<div class="row g-2">
<div class="col">
<input
id="ftp_limit"
class="form-control"
name="ftp_limit"
placeholder=""
type="number"
required=""
min="0"
data-validate
value="{{ plan_template_data.get('ftp', '') }}"
/>
</div>
<div class="col-auto align-self-center">
<span
class="form-help"
data-bs-toggle="popover"
data-bs-placement="top"
data-bs-content="<p class='mb-0'>Total number of FTP sub-accounts that can be created by user on this plan.</p>"
data-bs-html="true"
>?</span
>
</div>
</div>
</div>
</div>
<div class="col-lg-3">
<div class="mb-3">
<label class="form-label"
>Memory: <span class="text-danger">*</span></label
>
<div class="row g-2">
<div class="col">
<div class="input-group">
<input
id="ram"
class="form-control"
name="ram"
placeholder=""
type="number"
required=""
min="0"
data-validate
value="{{ plan_template_data.get('ram', '') }}"
/>
<div class="input-group-append">
<span class="input-group-text" id="ram-gb">GB</span>
</div>
</div>
</div>
<div class="col-auto align-self-center">
<span
class="form-help"
data-bs-toggle="popover"
data-bs-placement="top"
data-bs-content="<p class='mb-0'>Physical Memory (RAM) limit in GB.</p>"
data-bs-html="true"
>?</span
>
</div>
</div>
</div>
</div>
<div class="col-lg-3">
<div class="mb-3">
<label class="form-label"
>CPU: <span class="text-danger">*</span></label
>
<div class="row g-2">
<div class="col">
<div class="input-group">
<input
id="cpu"
class="form-control"
name="cpu"
placeholder=""
type="number"
required=""
min="0"
data-validate
value="{{ plan_template_data.get('cpu', '') }}"
/>
<div class="input-group-append">
<span class="input-group-text" id="cpu-cores"
>cores</span
>
</div>
</div>
</div>
<div class="col-auto align-self-center">
<span
class="form-help"
data-bs-toggle="popover"
data-bs-placement="top"
data-bs-content="<p class='mb-0'>Number of CPU cores.</p>"
data-bs-html="true"
>?</span
>
</div>
</div>
</div>
</div>
<div class="col-lg-3">
<div class="mb-3">
<label class="form-label"
>Port Speed: <span class="text-danger">*</span></label
>
<div class="row g-2">
<div class="col">
<div class="input-group">
<input
id="port_speed"
class="form-control"
name="port_speed"
placeholder=""
type="number"
name="port_speed"
required=""
min="1"
data-validate
value="{{ plan_template_data.get('port_speed', '') }}"
/>
<div class="input-group-append">
<span class="input-group-text" id="port-mbits"
>mbits</span
>
</div>
</div>
</div>
<div class="col-auto align-self-center">
<span
class="form-help"
data-bs-toggle="popover"
data-bs-placement="top"
data-bs-content="<p class='mb-0'>Port Speed in mbit/s (i.e., the network speed or bandwidth available to the user for network operations).</p>"
data-bs-html="true"
>?</span
>
</div>
</div>
</div>
</div>
<div class="col-lg-4">
<div class="mb-3">
<label class="form-label"
>Container size: <span class="text-danger">*</span></label
>
<div class="row g-2">
<div class="col">
<div class="input-group">
<input
id="disk_limit"
class="form-control"
name="disk_limit"
placeholder="10GB minimum"
type="number"
required=""
min="10"
data-validate
value="{{ plan_template_data.get('disk_limit_for_docker', '') }}"
/>
<div class="input-group-append">
<span class="input-group-text" id="disk-gb">GB</span>
</div>
</div>
</div>
<div class="col-auto align-self-center">
<span
class="form-help"
data-bs-toggle="popover"
data-bs-placement="top"
data-bs-content="<p>Total disk usage limit in GB for the user Docker container.</p><p class='mb-0'>This limit encompasses all user data <b>with the exception of the home directory</b>. This includes all system files, services, logs, MySQL databases, etc., Docker requires that the limit must be at least 10GB.</p>"
data-bs-html="true"
>?</span
>
</div>
</div>
</div>
</div>
<div class="col-lg-4">
<div class="mb-3">
<label class="form-label"
>Inodes limit: <span class="text-danger">*</span></label
>
<div class="row g-2">
<div class="col">
<input
id="inodes_limit"
class="form-control"
name="inodes_limit"
placeholder="minimum 500k"
type="number"
required=""
min="500000"
data-validate
value="{{ plan_template_data.get('inodes_for_storage_file', '') }}"
/>
</div>
<div class="col-auto align-self-center">
<span
class="form-help"
data-bs-toggle="popover"
data-bs-placement="top"
data-bs-content="<p>Total number of inodes for the users home directory.</p><p class='mb-0'>500.000 is the minimum.</p>"
data-bs-html="true"
>?</span
>
</div>
</div>
</div>
</div>
<div class="col-lg-4">
<div class="mb-3">
<label class="form-label"
>Storage file: <span class="text-danger">*</span></label
>
<div class="row g-2">
<div class="col">
<div class="input-group">
<input
id="storage_file"
class="form-control"
name="storage_file"
placeholder=""
type="number"
required=""
min="0"
data-validate
value="{{ plan_template_data.get('disk_limit_for_storage_file', '') }}"
/>
<div class="input-group-append">
<span class="input-group-text" id="disk-gb">GB</span>
</div>
</div>
</div>
<div class="col-auto align-self-center">
<span
class="form-help"
data-bs-toggle="popover"
data-bs-placement="top"
data-bs-content="<p>The maximum disk space allocation for the user's home directory in gigabytes.</p><p class='mb-0'>Enter 0 to allow unlimited disk space (recommended only for Virtual Private Servers operated by a single user).</p>"
data-bs-html="true"
>?</span
>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="modal-footer">
<a
href="#"
class="btn btn-link link-secondary"
data-bs-dismiss="modal"
>
Cancel
</a>
<button
type="button"
class="btn btn-primary ms-auto"
id="CreatePlanButton"
>
<!-- Download SVG icon from http://tabler-icons.io/i/plus -->
<svg
xmlns="http://www.w3.org/2000/svg"
class="icon"
width="24"
height="24"
viewBox="0 0 24 24"
stroke-width="2"
stroke="currentColor"
fill="none"
stroke-linecap="round"
stroke-linejoin="round"
>
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
<path d="M12 5l0 14"></path>
<path d="M5 12l14 0"></path>
</svg>
Create Plan
</button>
</div>
</form>
</div>
</div>
</div>
</div>
<!-- Page body -->
<div class="page-body">
{% if plans %}
<div class="container-xl">
<div class="card">
<div class="card-body p-0">
<div id="table-default" class="table-responsive">
<table class="table table-vcenter card-table table-striped">
<thead>
<tr>
<th><button class="table-sort" data-sort="sort-id">ID</button></th>
<th><button class="table-sort" data-sort="sort-name">Plan name</button></th>
<th><button class="table-sort" data-sort="sort-image" style="display: flex; justify-content: center; align-items: center;">Image</button></th>
<th><button class="table-sort" data-sort="sort-du">Container Size</button></th>
<th><button class="table-sort" data-sort="sort-storage">Storage</button></th>
<th style="display:none;"><button class="table-sort" data-sort="sort-storage-inodes">Inodes</button></th>
<th><button class="table-sort" data-sort="sort-domains">Domains</button></th>
<th><button class="table-sort" data-sort="sort-sites">Websites</button></th>
<th><button class="table-sort" data-sort="sort-emails">Emails</button></th>
<th><button class="table-sort" data-sort="sort-ftp">FTP</button></th>
<th><button class="table-sort" data-sort="sort-db">Databases</button></th>
<th><button class="table-sort" data-sort="sort-cpu">CPU</button></th>
<th><button class="table-sort" data-sort="sort-ram">RAM</button></th>
<th><button class="table-sort" data-sort="sort-port">Port Speed</button></th>
<th>&nbsp;</th>
</tr>
</thead>
<tbody class="table-tbody">
{% for plan in plans %}
<tr data-description="{{ plan.description }}" data-name="{{ plan.name }}" data-id="{{ plan.id }}" data-image="{{ plan.docker_image }}">
<td class="sort-id">{{ plan.id }}</td>
<td class="sort-name">
<div class="d-flex align-items-center gap-2">
<div>
<div>{{ plan.name }}</div>
<span class="text-secondary">{{ plan.description }}</span>
</div>
</div>
</td>
<td class="sort-image">
<div class="d-flex align-items-center">
{% if 'openpanel' in plan.docker_image %}
<div class="flex-fill">
<div class="text-reset mb-1" style="display: flex; justify-content: center; align-items: center;"><a href="/services/docker" class="text-reset"><svg version="1.0" style="vertical-align:middle;" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 213.000000 215.000000" preserveAspectRatio="xMidYMid meet"><g transform="translate(0.000000,215.000000) scale(0.100000,-0.100000)" fill="currentColor" stroke="none"><path d="M990 2071 c-39 -13 -141 -66 -248 -129 -53 -32 -176 -103 -272 -158 -206 -117 -276 -177 -306 -264 -17 -50 -19 -88 -19 -460 0 -476 0 -474 94 -568 55 -56 124 -98 604 -369 169 -95 256 -104 384 -37 104 54 532 303 608 353 76 50 126 113 147 184 8 30 12 160 12 447 0 395 -1 406 -22 461 -34 85 -98 138 -317 264 -104 59 -237 136 -295 170 -153 90 -194 107 -275 111 -38 2 -81 0 -95 -5z m205 -561 c66 -38 166 -95 223 -127 l102 -58 0 -262 c0 -262 0 -263 -22 -276 -13 -8 -52 -31 -88 -51 -36 -21 -126 -72 -200 -115 l-135 -78 -3 261 -3 261 -166 95 c-91 52 -190 109 -219 125 -30 17 -52 34 -51 39 3 9 424 256 437 255 3 0 59 -31 125 -69z"></path></g></svg> {{ plan.docker_image }}</a></div>
<div class="avatar-list avatar-list-stacked" style="display: flex; justify-content: center; align-items: center;">
<span class="avatar avatar-s rounded" data-bs-toggle="tooltip" data-bs-placement="top" title="Ubuntu">
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 100 100"><title>Ubuntu 24.04</title>
<circle fill="#f47421" cy="50" cx="50" r="45"/>
<circle fill="none" stroke="#ffffff" stroke-width="8.55" cx="50" cy="50" r="21.825"/>
<g id="friend"><circle fill="#f47421" cx="19.4" cy="50" r="8.4376"/>
<path stroke="#f47421" stroke-width="3.2378" d="M67,50H77"/>
<circle fill="#ffffff" cx="19.4" cy="50" r="6.00745"/></g>
<use xlink:href="#friend" transform="rotate(120,50,50)"/>
<use xlink:href="#friend" transform="rotate(240,50,50)"/></svg></span>
<span class="avatar avatar-s rounded" data-bs-toggle="tooltip" data-bs-placement="top" title="PHP">
<svg width="800px" height="800px" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg"><title>PHP 8.2</title>
<circle cx="16" cy="16" r="14" fill="#8892BF"/>
<path d="M14.4392 10H16.1192L15.6444 12.5242H17.154C17.9819 12.5419 18.5986 12.7269 19.0045 13.0793C19.4184 13.4316 19.5402 14.1014 19.3698 15.0881L18.5541 19.4889H16.8497L17.6288 15.2863C17.7099 14.8457 17.6856 14.533 17.5558 14.348C17.426 14.163 17.146 14.0705 16.7158 14.0705L15.3644 14.0573L14.3661 19.4889H12.6861L14.4392 10Z" fill="white"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M6.74092 12.5243H10.0036C10.9612 12.533 11.6552 12.8327 12.0854 13.4229C12.5156 14.0132 12.6576 14.8193 12.5115 15.8414C12.4548 16.3085 12.3289 16.7665 12.1341 17.2159C11.9474 17.6652 11.6878 18.0704 11.355 18.4317C10.9491 18.8898 10.5149 19.1805 10.0523 19.304C9.58969 19.4274 9.11076 19.489 8.61575 19.489H7.15484L6.69222 22H5L6.74092 12.5243ZM7.43485 17.9956L8.16287 14.0441H8.40879C8.49815 14.0441 8.5914 14.0396 8.6888 14.0309C9.33817 14.0221 9.87774 14.0882 10.308 14.2291C10.7462 14.37 10.8923 14.9031 10.7462 15.8282C10.5678 16.9296 10.2186 17.5727 9.69926 17.7577C9.1799 17.934 8.53053 18.0176 7.75138 18.0088H7.58094C7.53224 18.0088 7.48355 18.0043 7.43485 17.9956Z" fill="white"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M24.4365 12.5243H21.1738L19.4329 22H21.1251L21.5878 19.489H23.0487C23.5437 19.489 24.0226 19.4274 24.4852 19.304C24.9479 19.1805 25.382 18.8898 25.7879 18.4317C26.1207 18.0704 26.3803 17.6652 26.567 17.2159C26.7618 16.7665 26.8877 16.3085 26.9444 15.8414C27.0905 14.8193 26.9486 14.0132 26.5183 13.4229C26.0881 12.8327 25.3942 12.533 24.4365 12.5243ZM22.5958 14.0441L21.8678 17.9956C21.9165 18.0043 21.9652 18.0088 22.0139 18.0088H22.1843C22.9635 18.0176 23.6128 17.934 24.1322 17.7577C24.6515 17.5727 25.0007 16.9296 25.1792 15.8282C25.3253 14.9031 25.1792 14.37 24.7409 14.2291C24.3107 14.0882 23.7711 14.0221 23.1217 14.0309C23.0243 14.0396 22.9311 14.0441 22.8417 14.0441H22.5958Z" fill="white"/>
</svg></span>
{% if 'nginx' in plan.docker_image %}
<span class="avatar avatar-s rounded" data-bs-toggle="tooltip" data-bs-placement="top" title="Nginx">
<svg width="800px" height="800px" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"><title>Nginx 1.24</title><path d="M15.948,2h.065a10.418,10.418,0,0,1,.972.528Q22.414,5.65,27.843,8.774a.792.792,0,0,1,.414.788c-.008,4.389,0,8.777-.005,13.164a.813.813,0,0,1-.356.507q-5.773,3.324-11.547,6.644a.587.587,0,0,1-.657.037Q9.912,26.6,4.143,23.274a.7.7,0,0,1-.4-.666q0-6.582,0-13.163a.693.693,0,0,1,.387-.67Q9.552,5.657,14.974,2.535c.322-.184.638-.379.974-.535" style="fill:#019639"/><path d="M8.767,10.538q0,5.429,0,10.859a1.509,1.509,0,0,0,.427,1.087,1.647,1.647,0,0,0,2.06.206,1.564,1.564,0,0,0,.685-1.293c0-2.62-.005-5.24,0-7.86q3.583,4.29,7.181,8.568a2.833,2.833,0,0,0,2.6.782,1.561,1.561,0,0,0,1.251-1.371q.008-5.541,0-11.081a1.582,1.582,0,0,0-3.152,0c0,2.662-.016,5.321,0,7.982-2.346-2.766-4.663-5.556-7-8.332A2.817,2.817,0,0,0,10.17,9.033,1.579,1.579,0,0,0,8.767,10.538Z" style="fill:#fff"/></svg></span>
{% elif 'apache' in plan.docker_image %}
<span class="avatar avatar-s rounded" data-bs-toggle="tooltip" data-bs-placement="top" title="Apache">
<svg width="800px" height="800px" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><defs><linearGradient id="a" x1="-5602.682" y1="768.541" x2="-5598.727" y2="763.917" gradientTransform="matrix(0.423, -0.906, -0.906, -0.423, 3082.853, -4748.551)" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#f69923"/><stop offset="0.312" stop-color="#f79a23"/><stop offset="0.838" stop-color="#e97826"/></linearGradient><linearGradient id="b" x1="-5631.952" y1="769.052" x2="-5603.737" y2="769.052" gradientTransform="matrix(0.423, -0.906, -0.906, -0.423, 3082.853, -4748.551)" gradientUnits="userSpaceOnUse"><stop offset="0.323" stop-color="#9e2064"/><stop offset="0.63" stop-color="#c92037"/><stop offset="0.751" stop-color="#cd2335"/><stop offset="1" stop-color="#e97826"/></linearGradient><linearGradient id="c" x1="-5628.546" y1="766.221" x2="-5611.733" y2="766.221" gradientTransform="matrix(0.423, -0.906, -0.906, -0.423, 3082.853, -4748.551)" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#282662"/><stop offset="0.095" stop-color="#662e8d"/><stop offset="0.788" stop-color="#9f2064"/><stop offset="0.949" stop-color="#cd2032"/></linearGradient><linearGradient id="d" x1="-5630.367" y1="769.316" x2="-5602.152" y2="769.316" xlink:href="#b"/><linearGradient id="e" x1="-5628.31" y1="768.933" x2="-5613.482" y2="768.933" xlink:href="#c"/><linearGradient id="f" x1="-5630.367" y1="766.394" x2="-5602.152" y2="766.394" xlink:href="#b"/><linearGradient id="g" x1="-5632.118" y1="766.539" x2="-5603.902" y2="766.539" xlink:href="#b"/><linearGradient id="h" x1="-5630.367" y1="765.526" x2="-5602.152" y2="765.526" xlink:href="#b"/><linearGradient id="i" x1="-5630.367" y1="765.625" x2="-5602.152" y2="765.625" xlink:href="#b"/><linearGradient id="j" x1="-5614.516" y1="765.645" x2="-5608.28" y2="765.645" xlink:href="#b"/></defs><title>Apache 2.4</title><path d="M19.993,2.133a10.267,10.267,0,0,0-2.04,2.056l.8,1.51a19.733,19.733,0,0,1,1.708-2.144c.044-.049.068-.072.068-.072l-.068.072a17.865,17.865,0,0,0-1.6,2.174,30.1,30.1,0,0,0,3.111-.385,3.288,3.288,0,0,0-.3-2.5S20.9,1.6,19.993,2.133Z" style="fill:url(#a)"/><path d="M18.638,10.749l.018,0-.115.013-.021.009Z" style="fill:none"/><path d="M17.821,13.455c-.066.015-.132.026-.2.036C17.689,13.481,17.756,13.469,17.821,13.455Z" style="fill:none"/><path d="M12.27,19.524c.009-.023.017-.045.025-.068q.264-.7.523-1.357.291-.741.575-1.436.3-.733.59-1.418.305-.718.6-1.384.24-.542.474-1.049.078-.169.155-.335.153-.329.3-.645.138-.291.274-.57c.03-.062.06-.123.09-.185l.015-.03-.1.011-.078-.154c-.007.015-.015.03-.023.045q-.211.418-.417.845-.119.246-.238.495-.328.689-.645,1.389T13.766,15.1q-.3.7-.594,1.4t-.567,1.387q-.289.72-.562,1.426-.062.159-.123.318-.219.569-.426,1.124l.123.243.11-.012.012-.033Q12.008,20.216,12.27,19.524Z" style="fill:none"/><path d="M17.57,13.5Z" style="fill:none"/><path d="M17.305,14.818l-.315.055h0l.161-.025C17.2,14.839,17.253,14.829,17.305,14.818Z" style="fill:#be202e"/><path d="M17.305,14.818l-.315.055h0l.161-.025C17.2,14.839,17.253,14.829,17.305,14.818Z" style="fill:#be202e;opacity:0.3499999940395355;isolation:isolate"/><path d="M17.573,13.5h0l.05-.007c.068-.01.135-.022.2-.036l-.248.042Z" style="fill:#be202e"/><path d="M17.573,13.5h0l.05-.007c.068-.01.135-.022.2-.036l-.248.042Z" style="fill:#be202e;opacity:0.3499999940395355;isolation:isolate"/><path d="M16.394,9.6q.365-.682.739-1.332.388-.674.784-1.305l.046-.075q.392-.62.79-1.191l-.8-1.51-.182.225c-.231.288-.47.6-.716.925-.277.37-.562.764-.851,1.179-.267.383-.538.784-.809,1.2-.231.353-.462.717-.692,1.09l-.026.042L15.718,10.9Q16.052,10.244,16.394,9.6Z" style="fill:url(#b)"/><path d="M11.651,21.192q-.207.568-.415,1.159l-.006.017-.059.167c-.093.266-.175.5-.361,1.049a2.107,2.107,0,0,1,.786.926,1.68,1.68,0,0,0-.544-1.154,3.621,3.621,0,0,0,3.489-1.42,2.791,2.791,0,0,0,.165-.313,1.451,1.451,0,0,1-1.4.513l0,0,0,0a4.059,4.059,0,0,0,2.046-1.672c.111-.178.218-.372.328-.587a3.3,3.3,0,0,1-3.109,1.01l-.842.092C11.7,21.05,11.677,21.121,11.651,21.192Z" style="fill:url(#c)"/><path d="M12.044,19.306q.273-.706.562-1.426.276-.69.567-1.387t.594-1.4q.308-.711.629-1.419t.645-1.389q.118-.248.238-.495.207-.426.417-.845c.007-.015.015-.03.023-.045L14.677,8.847l-.051.083c-.242.4-.484.8-.721,1.216s-.475.844-.7,1.276q-.291.547-.568,1.1l-.11.225c-.227.467-.432.918-.617,1.352q-.315.737-.556,1.406c-.107.293-.2.576-.292.847-.073.232-.142.464-.208.7q-.234.818-.4,1.631L11.5,20.748q.208-.554.426-1.124Z" style="fill:url(#d)"/><path d="M10.435,18.755a16.07,16.07,0,0,0-.272,1.974c0,.023,0,.046-.005.069a4.15,4.15,0,0,0-1.2-1.029,5.825,5.825,0,0,1,1.172,2.693,2.642,2.642,0,0,1-1.325-.226,2.662,2.662,0,0,0,1.13.686,3.247,3.247,0,0,0-1.571.783,3.359,3.359,0,0,1,1.861-.342C9.51,25.389,8.793,27.626,8.076,30a.625.625,0,0,0,.425-.413c.128-.43.976-3.251,2.306-6.959l.115-.318.032-.089q.211-.583.437-1.19l.1-.277,0-.005L10.45,18.684C10.445,18.707,10.439,18.731,10.435,18.755Z" style="fill:url(#e)"/><path d="M15.88,11.078l-.09.185q-.135.279-.274.57-.15.315-.3.645c-.052.111-.1.222-.155.335q-.234.508-.474,1.049-.3.666-.6,1.384-.291.685-.59,1.418-.284.7-.575,1.436-.259.661-.523,1.357c-.009.023-.017.045-.025.068q-.262.693-.529,1.422l-.012.033.842-.092-.05-.009a6,6,0,0,0,3.21-1.807,7.984,7.984,0,0,0,1.1-1.524,13.139,13.139,0,0,0,.706-1.467c.195-.472.382-.982.562-1.536a3.053,3.053,0,0,1-.788.274c-.051.011-.1.021-.156.03s-.107.018-.161.025h0a3.668,3.668,0,0,0,1.962-1.913,3.344,3.344,0,0,1-1.13.495c-.066.015-.132.026-.2.036l-.05.007h0a3.821,3.821,0,0,0,.839-.469c.051-.038.1-.078.148-.12.073-.063.142-.129.208-.2.042-.044.083-.09.123-.138a3.27,3.27,0,0,0,.263-.362c.025-.04.05-.08.074-.122.031-.06.061-.119.09-.178.131-.264.236-.5.319-.706.042-.1.078-.2.109-.288.013-.035.025-.07.036-.1.033-.1.06-.187.081-.265a2.605,2.605,0,0,0,.062-.275h0a1.01,1.01,0,0,1-.109.075,3.965,3.965,0,0,1-1.162.4l.773-.085-.773.085-.018,0-.119.019.021-.009-2.645.29Z" style="fill:url(#f)"/><path d="M18.858,5.73c-.235.361-.492.771-.768,1.236l-.044.074q-.358.6-.759,1.327-.346.626-.719,1.347-.326.629-.672,1.336l2.645-.29A3.216,3.216,0,0,0,19.99,9.62c.089-.128.178-.262.267-.4.272-.424.538-.891.776-1.355a14.074,14.074,0,0,0,.588-1.294,6.8,6.8,0,0,0,.233-.7c.048-.184.086-.358.115-.524A30.152,30.152,0,0,1,18.858,5.73Z" style="fill:url(#g)"/><path d="M17.149,14.848c-.053.009-.107.018-.161.025h0C17.042,14.866,17.1,14.857,17.149,14.848Z" style="fill:#be202e"/><path d="M17.149,14.848c-.053.009-.107.018-.161.025h0C17.042,14.866,17.1,14.857,17.149,14.848Z" style="fill:#be202e;opacity:0.3499999940395355;isolation:isolate"/><path d="M17.149,14.848c-.053.009-.107.018-.161.025h0C17.042,14.866,17.1,14.857,17.149,14.848Z" style="fill:url(#h)"/><path d="M17.57,13.5l.05-.007-.05.007Z" style="fill:#be202e"/><path d="M17.57,13.5l.05-.007-.05.007Z" style="fill:#be202e;opacity:0.3499999940395355;isolation:isolate"/><path d="M17.57,13.5l.05-.007-.05.007Z" style="fill:url(#i)"/><path d="M17.572,13.5h0Z" style="fill:#be202e"/><path d="M17.572,13.5h0Z" style="fill:#be202e;opacity:0.3499999940395355;isolation:isolate"/><path d="M17.572,13.5h0Z" style="fill:url(#j)"/></svg></span>
{% elif 'litespeed' in plan.docker_image %}
<span class="avatar avatar-s rounded" data-bs-toggle="tooltip" data-bs-placement="top" title="LiteSpeed"><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-bolt"><title>LiteSpeed</title><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M13 3l0 7l6 0l-8 11l0 -7l-6 0l8 -11" /></svg></span>
{% endif %}
{% if 'mysql' in plan.docker_image %}
<span class="avatar avatar-s rounded" data-bs-toggle="tooltip" data-bs-placement="top" title="MySQL">
<svg width="800px" height="800px" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"><title>MySQL 8</title><path d="M8.785,6.865a3.055,3.055,0,0,0-.785.1V7h.038a6.461,6.461,0,0,0,.612.785c.154.306.288.611.441.917.019-.019.038-.039.038-.039a1.074,1.074,0,0,0,.4-.957,4.314,4.314,0,0,1-.23-.4c-.115-.191-.364-.287-.517-.44" style="fill:#5d87a1;fill-rule:evenodd"/><path d="M27.78,23.553a8.849,8.849,0,0,0-3.712.536c-.287.115-.745.115-.785.478.154.153.172.4.307.613a4.467,4.467,0,0,0,.995,1.167c.4.306.8.611,1.225.879.745.461,1.588.728,2.314,1.187.422.268.842.612,1.264.9.21.153.343.4.611.5v-.058a3.844,3.844,0,0,0-.291-.613c-.191-.19-.383-.363-.575-.554a9.118,9.118,0,0,0-1.99-1.932c-.613-.422-1.953-1-2.2-1.7l-.039-.039a7.69,7.69,0,0,0,1.321-.308c.65-.172,1.243-.133,1.912-.3.307-.077.862-.268.862-.268v-.3c-.342-.34-.587-.795-.947-1.116a25.338,25.338,0,0,0-3.122-2.328c-.587-.379-1.344-.623-1.969-.946-.226-.114-.6-.17-.737-.36a7.594,7.594,0,0,1-.776-1.457c-.548-1.04-1.079-2.193-1.551-3.293a20.236,20.236,0,0,0-.965-2.157A19.078,19.078,0,0,0,11.609,5a9.07,9.07,0,0,0-2.421-.776c-.474-.02-.946-.057-1.419-.075A7.55,7.55,0,0,1,6.9,3.485C5.818,2.8,3.038,1.328,2.242,3.277,1.732,4.508,3,5.718,3.435,6.343A8.866,8.866,0,0,1,4.4,7.762c.133.322.171.663.3,1A22.556,22.556,0,0,0,5.687,11.3a8.946,8.946,0,0,0,.7,1.172c.153.209.417.3.474.645a5.421,5.421,0,0,0-.436,1.419,8.336,8.336,0,0,0,.549,6.358c.3.473,1.022,1.514,1.987,1.116.851-.34.662-1.419.908-2.364.056-.229.019-.379.132-.53V19.3s.483,1.061.723,1.6a10.813,10.813,0,0,0,2.4,2.59A3.514,3.514,0,0,1,14,24.657V25h.427A1.054,1.054,0,0,0,14,24.212a9.4,9.4,0,0,1-.959-1.16,24.992,24.992,0,0,1-2.064-3.519c-.3-.6-.553-1.258-.793-1.857-.11-.231-.11-.58-.295-.7a7.266,7.266,0,0,0-.884,1.313,11.419,11.419,0,0,0-.517,2.921c-.073.02-.037,0-.073.038-.589-.155-.792-.792-1.014-1.332a8.756,8.756,0,0,1-.166-5.164c.128-.405.683-1.681.461-2.068-.111-.369-.48-.58-.682-.871a7.767,7.767,0,0,1-.663-1.237C5.912,9.5,5.69,8.3,5.212,7.216a10.4,10.4,0,0,0-.921-1.489A9.586,9.586,0,0,1,3.276,4.22c-.092-.213-.221-.561-.074-.793a.3.3,0,0,1,.259-.252c.238-.212.921.058,1.16.174a9.2,9.2,0,0,1,1.824.967c.258.194.866.685.866.685h.18c.612.133,1.3.037,1.876.21a12.247,12.247,0,0,1,2.755,1.32,16.981,16.981,0,0,1,5.969,6.545c.23.439.327.842.537,1.3.4.94.9,1.9,1.3,2.814a12.578,12.578,0,0,0,1.36,2.564c.286.4,1.435.612,1.952.822a13.7,13.7,0,0,1,1.32.535c.651.4,1.3.861,1.913,1.3.305.23,1.262.708,1.32,1.091" style="fill:#00758f;fill-rule:evenodd"/></svg></span>
{% elif 'mariadb' in plan.docker_image %}
<span class="avatar avatar-s rounded" data-bs-toggle="tooltip" data-bs-placement="top" title="MariaDB" >
<svg width="800px" height="800px" viewBox="0 -43 256 256" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" preserveAspectRatio="xMidYMid"><title>MariaDB 10</title>
<g>
<path d="M250.382523,0.00447241672 C246.426131,0.130891567 247.677353,1.27087056 239.128415,3.37469592 C230.495553,5.49917829 219.950359,4.84773528 210.654095,8.74649903 C182.903099,20.3847485 177.335232,60.1626339 152.106938,74.4118517 C133.249415,85.0635193 114.223916,85.9130759 97.1188786,91.2730771 C85.8778244,94.7980074 73.5811418,102.026905 63.3964279,110.803626 C55.49096,117.618586 55.2845466,123.610697 47.0245784,132.158212 C38.1894743,141.300822 11.9101646,132.312705 0,146.305625 C3.83670733,150.185042 5.51875114,151.271649 13.0796841,150.265122 C11.5142932,153.232113 2.28663486,155.732479 4.09296236,160.097129 C5.99360595,164.689675 28.3022154,167.802917 48.5816837,155.559279 C58.0261053,149.857249 65.5486285,141.638595 80.2576532,139.676806 C99.2917078,137.139881 121.218611,141.30404 143.253683,144.481588 C139.986431,154.22355 133.426672,160.702176 128.172006,168.461009 C126.544787,170.213508 131.440311,170.409956 137.025262,169.350783 C147.071883,166.866533 154.312169,164.86632 161.894457,160.453039 C171.209327,155.030397 172.62088,141.127864 184.04984,138.119701 C190.417778,147.907219 207.737102,150.219223 218.48411,142.390618 C209.053925,139.721295 206.447626,119.648695 209.630855,110.803626 C212.646122,102.431204 215.625486,89.0383196 218.662065,77.9709494 C221.922199,66.0849867 223.124932,51.1038191 227.070434,45.0492956 C233.00651,35.9401552 239.565643,32.81205 245.260156,27.675489 C250.954656,22.538928 256.166954,17.538894 255.995904,5.78538669 C255.940809,1.99964564 253.983391,-0.11060033 250.382523,0.00447241672 L250.382523,0.00447241672 Z" fill="#002B64">
</path>
<path d="M241.905484,6.96809574 C242.853676,10.2001831 244.337002,11.6835082 250.750076,12.2768382 C249.813239,20.407447 244.389521,24.8545834 238.308598,29.1214497 C232.957272,32.8744751 227.094944,36.4883945 223.327724,42.3507224 C219.46824,48.3564147 217.01827,68.9100487 211.033869,89.2081817 C205.861394,106.746904 198.050161,124.088323 184.409248,131.686638 C182.98412,128.099688 184.590937,121.479374 181.756296,119.303358 C179.922367,124.53403 177.848551,129.524816 175.419872,134.163578 C167.415594,149.462409 155.564607,160.917369 135.760443,164.414894 C145.157201,151.699462 154.142319,138.568131 154.336783,116.651825 C147.723566,118.082631 147.864092,133.703676 141.069185,137.879698 C136.712894,138.353794 132.299824,138.350955 127.858366,138.084099 C109.618435,136.991122 90.9072468,131.509207 73.84404,136.984025 C62.2258429,140.71292 52.7240456,149.509251 42.8858386,153.776117 C31.323,158.791033 22.5664139,160.853494 8.16751449,158.791033 C6.33926307,156.328288 18.7055102,153.150139 17.9659769,147.803072 C12.3307609,147.179933 9.058929,148.545444 4.16040754,146.319747 C4.70121793,145.323293 5.49610985,144.492915 6.49682201,143.801643 C15.4748424,137.587291 40.9766785,142.333932 47.8013935,135.632709 C52.0143206,131.499271 54.7779895,127.172788 57.6396004,122.966958 C60.4146249,118.886039 63.2833331,114.918677 67.6538192,111.343083 C69.2677337,110.022994 71.0221737,108.71852 72.8844919,107.445273 C80.3323453,102.348029 89.5459944,97.7248808 98.6134401,94.5382159 C110.965493,90.1961188 123.482202,89.8384174 136.647599,84.8078871 C144.781047,81.6992919 153.625639,77.8596801 160.835025,72.4870623 C162.546881,71.2095575 164.166473,69.8483051 165.663993,68.3891106 C186.250274,48.3209285 190.331193,12.9212684 222.449085,9.62246697 C226.3327,9.22360156 229.512267,9.3527715 232.406525,9.26476561 C235.742233,9.16540412 238.694688,8.77789431 241.905484,6.96809574 Z M202.75118,120.267107 C203.134432,126.40197 206.695831,138.573752 209.839913,141.531886 C203.682339,143.029405 193.074791,140.555304 190.353705,136.211788 C191.751863,129.940658 199.027963,124.2075 202.75118,120.267107 Z" fill="#C49A6C" fill-rule="nonzero">
</path>
<path d="M244.218787,13.8370641 C242.980829,16.4335799 240.610981,19.7812981 240.610981,26.3910072 C240.60081,27.5258023 239.749351,28.3031588 239.734821,26.5537435 C239.798753,20.0936937 241.508937,17.3010225 243.32519,13.6307377 C244.169385,12.12688 244.677936,12.7473121 244.218787,13.8370641 Z M242.972111,12.8591933 C241.511843,15.3365629 237.995576,19.8554012 237.414375,26.4404093 C237.306853,27.5693924 236.388555,28.2682867 236.528044,26.5232305 C237.161553,20.0951467 239.97166,16.0717822 242.104668,12.5744048 C243.072368,11.1519152 243.527158,11.8144844 242.972111,12.8591933 Z M241.835862,11.5631149 C240.172174,13.9082613 234.759739,19.3352263 233.62785,25.8490372 C233.42443,26.9634903 232.450918,27.5853754 232.73716,25.8577553 C233.90828,19.5037746 238.573871,14.5098044 240.993121,11.2071293 C242.077061,9.86891382 242.473731,10.5678081 241.835862,11.5631149 Z M240.821667,10.1173773 L240.274318,10.6995682 C237.854262,13.2941372 232.232203,19.6224619 230.358594,25.4145894 C229.99825,26.4898114 228.947729,26.9693023 229.475169,25.2983492 C231.526809,19.17249 237.177536,12.5744048 240.037045,9.64515141 C241.299704,8.47257825 241.593211,9.22087463 240.821667,10.1173773 Z M211.771784,23.2321794 C213.025725,17.8458985 217.214732,15.391777 224.446326,15.9904141 C226.191383,24.0298779 216.425752,27.2729799 211.771784,23.2321794 Z" fill="#002B64">
</path>
</g>
</svg></span>
{% else %}
<span class="avatar avatar-s rounded" data-bs-toggle="tooltip" data-bs-placement="top" title="MySQL">
<svg width="800px" height="800px" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"><title>MySQL 8</title><path d="M8.785,6.865a3.055,3.055,0,0,0-.785.1V7h.038a6.461,6.461,0,0,0,.612.785c.154.306.288.611.441.917.019-.019.038-.039.038-.039a1.074,1.074,0,0,0,.4-.957,4.314,4.314,0,0,1-.23-.4c-.115-.191-.364-.287-.517-.44" style="fill:#5d87a1;fill-rule:evenodd"/><path d="M27.78,23.553a8.849,8.849,0,0,0-3.712.536c-.287.115-.745.115-.785.478.154.153.172.4.307.613a4.467,4.467,0,0,0,.995,1.167c.4.306.8.611,1.225.879.745.461,1.588.728,2.314,1.187.422.268.842.612,1.264.9.21.153.343.4.611.5v-.058a3.844,3.844,0,0,0-.291-.613c-.191-.19-.383-.363-.575-.554a9.118,9.118,0,0,0-1.99-1.932c-.613-.422-1.953-1-2.2-1.7l-.039-.039a7.69,7.69,0,0,0,1.321-.308c.65-.172,1.243-.133,1.912-.3.307-.077.862-.268.862-.268v-.3c-.342-.34-.587-.795-.947-1.116a25.338,25.338,0,0,0-3.122-2.328c-.587-.379-1.344-.623-1.969-.946-.226-.114-.6-.17-.737-.36a7.594,7.594,0,0,1-.776-1.457c-.548-1.04-1.079-2.193-1.551-3.293a20.236,20.236,0,0,0-.965-2.157A19.078,19.078,0,0,0,11.609,5a9.07,9.07,0,0,0-2.421-.776c-.474-.02-.946-.057-1.419-.075A7.55,7.55,0,0,1,6.9,3.485C5.818,2.8,3.038,1.328,2.242,3.277,1.732,4.508,3,5.718,3.435,6.343A8.866,8.866,0,0,1,4.4,7.762c.133.322.171.663.3,1A22.556,22.556,0,0,0,5.687,11.3a8.946,8.946,0,0,0,.7,1.172c.153.209.417.3.474.645a5.421,5.421,0,0,0-.436,1.419,8.336,8.336,0,0,0,.549,6.358c.3.473,1.022,1.514,1.987,1.116.851-.34.662-1.419.908-2.364.056-.229.019-.379.132-.53V19.3s.483,1.061.723,1.6a10.813,10.813,0,0,0,2.4,2.59A3.514,3.514,0,0,1,14,24.657V25h.427A1.054,1.054,0,0,0,14,24.212a9.4,9.4,0,0,1-.959-1.16,24.992,24.992,0,0,1-2.064-3.519c-.3-.6-.553-1.258-.793-1.857-.11-.231-.11-.58-.295-.7a7.266,7.266,0,0,0-.884,1.313,11.419,11.419,0,0,0-.517,2.921c-.073.02-.037,0-.073.038-.589-.155-.792-.792-1.014-1.332a8.756,8.756,0,0,1-.166-5.164c.128-.405.683-1.681.461-2.068-.111-.369-.48-.58-.682-.871a7.767,7.767,0,0,1-.663-1.237C5.912,9.5,5.69,8.3,5.212,7.216a10.4,10.4,0,0,0-.921-1.489A9.586,9.586,0,0,1,3.276,4.22c-.092-.213-.221-.561-.074-.793a.3.3,0,0,1,.259-.252c.238-.212.921.058,1.16.174a9.2,9.2,0,0,1,1.824.967c.258.194.866.685.866.685h.18c.612.133,1.3.037,1.876.21a12.247,12.247,0,0,1,2.755,1.32,16.981,16.981,0,0,1,5.969,6.545c.23.439.327.842.537,1.3.4.94.9,1.9,1.3,2.814a12.578,12.578,0,0,0,1.36,2.564c.286.4,1.435.612,1.952.822a13.7,13.7,0,0,1,1.32.535c.651.4,1.3.861,1.913,1.3.305.23,1.262.708,1.32,1.091" style="fill:#00758f;fill-rule:evenodd"/></svg></span>
{% endif %}
</div>
</div>
</div>
{% else %}
<div class="flex-fill">
<div class="text-reset mb-1" style="display: flex; justify-content: center; align-items: center;"><a href="/services/docker" class="text-reset"><svg width="24px" height="24px" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg">
<circle cx="512" cy="512" r="512" style="fill:#0091e2"/>
<path d="M827.3 461.5c-1.6-1.3-16.1-12.2-46.7-12.2-8.1 0-16.2.6-24.2 2.1-5.9-40.7-39.5-60.5-41-61.4l-8.2-4.8-5.4 7.8c-6.8 10.5-11.7 22-14.6 34.2-5.5 23.2-2.2 45 9.6 63.6-14.2 7.9-37.1 9.9-41.7 10H277c-9.9 0-17.9 8-17.9 17.9-.4 33.1 5.2 66 16.5 97.1 13 34.2 32.4 59.3 57.6 74.7 28.2 17.3 74.1 27.2 126.2 27.2 23.5.1 47-2.1 70.1-6.4 32.1-5.9 63-17.1 91.4-33.2 23.4-13.6 44.5-30.8 62.4-51.1 29.9-33.9 47.8-71.7 61.1-105.2h5.3c32.8 0 53-13.1 64.1-24.1 7.4-7 13.2-15.5 16.9-25l2.3-6.9-5.7-4.3zM312 489.9h50.7c2.4 0 4.4-2 4.4-4.4v-45.1c0-2.4-2-4.4-4.4-4.5H312c-2.4 0-4.4 2-4.4 4.4v45.2c0 2.5 2 4.4 4.4 4.4m69.9 0h50.7c2.4 0 4.4-2 4.4-4.4v-45.1c0-2.4-2-4.4-4.4-4.5h-50.7c-2.5 0-4.5 2-4.5 4.5v45.1c0 2.5 2 4.4 4.5 4.4m70.8.1h50.7c2.4 0 4.4-2 4.4-4.4v-45.1c0-2.4-2-4.4-4.4-4.5h-50.7c-2.4 0-4.4 2-4.4 4.4v45.2c0 2.4 2 4.3 4.4 4.4m70.1 0h50.7c2.4 0 4.4-2 4.5-4.4v-45.1c0-2.5-2-4.5-4.5-4.5h-50.7c-2.4 0-4.4 2-4.4 4.4v45.2c0 2.4 1.9 4.4 4.4 4.4m-141-65h50.7c2.4 0 4.4-2 4.4-4.5v-45.1c0-2.4-2-4.4-4.4-4.4h-50.7c-2.5 0-4.4 2-4.5 4.4v45.1c.1 2.5 2.1 4.5 4.5 4.5m70.9 0h50.7c2.4 0 4.4-2 4.4-4.5v-45.1c0-2.4-2-4.4-4.4-4.4h-50.7c-2.4 0-4.4 2-4.4 4.4v45.1c0 2.5 2 4.5 4.4 4.5m70.1 0h50.7c2.5 0 4.4-2 4.5-4.5v-45.1c0-2.5-2-4.4-4.5-4.4h-50.7c-2.4 0-4.4 2-4.4 4.4v45.1c0 2.5 1.9 4.5 4.4 4.5m0-64.9h50.7c2.5 0 4.5-2 4.5-4.5v-45.2c0-2.4-2-4.4-4.5-4.4h-50.7c-2.4 0-4.4 2-4.4 4.4v45.2c0 2.5 1.9 4.5 4.4 4.5M593.4 490h50.7c2.4 0 4.4-2 4.4-4.4v-45.1c0-2.5-2-4.4-4.4-4.5h-50.7c-2.4 0-4.4 2-4.4 4.4v45.2c0 2.4 2 4.4 4.4 4.4" style="fill:#fff"/></svg> {{ plan.docker_image }}</a></div>
<div style="display: flex; justify-content: center; align-items: center;">
<span class="badge bg-teal text-teal-fg ms-2">custom image</span>
</div>
</div>
{% endif %}
</td>
<td class="sort-du">{% if plan.disk_limit == 0 %}∞{% else %}{{ plan.disk_limit }}{% endif %}</td>
<td class="sort-storage">{% if plan.storage_file == "0 GB" or plan.storage_file is none %}∞{% else %}{{ plan.storage_file }}{% endif %}</td>
<td class="d-none sort-storage-inodes">{% if plan.inodes_limit == "0" or plan.inodes_limit is none %}∞{% else %}{{ plan.inodes_limit }}{% endif %}</td>
<td class="sort-domains">{% if plan.domains_limit == 0 %}∞{% else %}{{ plan.domains_limit }}{% endif %}</td>
<td class="sort-sites">{% if plan.websites_limit == 0 %}∞{% else %}{{ plan.websites_limit }}{% endif %}</td>
<td class="sort-emails">{% if plan.email_limit == 0 %}∞{% else %}{{ plan.email_limit }}{% endif %}</td>
<td class="sort-ftp">{% if plan.ftp_limit == 0 %}∞{% else %}{{ plan.ftp_limit }}{% endif %}</td>
<td class="sort-db">{% if plan.db_limit == 0 %}∞{% else %}{{ plan.db_limit }}{% endif %}</td>
<td class="sort-cpu">{% if plan.cpu == 0 %}∞<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-cpu-2"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M5 5m0 1a1 1 0 0 1 1 -1h12a1 1 0 0 1 1 1v12a1 1 0 0 1 -1 1h-12a1 1 0 0 1 -1 -1z" /><path d="M8 10v-2h2m6 6v2h-2m-4 0h-2v-2m8 -4v-2h-2" /><path d="M3 10h2" /><path d="M3 14h2" /><path d="M10 3v2" /><path d="M14 3v2" /><path d="M21 10h-2" /><path d="M21 14h-2" /><path d="M14 21v-2" /><path d="M10 21v-2" /></svg>{% else %}{{ plan.cpu }}<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-cpu"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M5 5m0 1a1 1 0 0 1 1 -1h12a1 1 0 0 1 1 1v12a1 1 0 0 1 -1 1h-12a1 1 0 0 1 -1 -1z" /><path d="M9 9h6v6h-6z" /><path d="M3 10h2" /><path d="M3 14h2" /><path d="M10 3v2" /><path d="M14 3v2" /><path d="M21 10h-2" /><path d="M21 14h-2" /><path d="M14 21v-2" /><path d="M10 21v-2" /></svg>{% endif %}</td>
<td class="sort-ram">{% if plan.ram == '0 g' %}∞<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-badge-sd"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M19 4a3 3 0 0 1 3 3v10a3 3 0 0 1 -3 3h-14a3 3 0 0 1 -3 -3v-10a3 3 0 0 1 3 -3zm-4 4h-1a1 1 0 0 0 -1 1v6a1 1 0 0 0 1 1h1a3 3 0 0 0 3 -3v-2a3 3 0 0 0 -3 -3m-5.75 0h-1.25a2 2 0 0 0 -2 2v1a2 2 0 0 0 2 2h1v1h-1.033l-.025 -.087a1 1 0 0 0 -1.942 .337c0 .966 .784 1.75 1.75 1.75h1.25a2 2 0 0 0 2 -2v-1a2 2 0 0 0 -2 -2h-1v-1h1.032l.026 .087a1 1 0 0 0 1.942 -.337a1.75 1.75 0 0 0 -1.75 -1.75m5.75 2a1 1 0 0 1 1 1v2a1 1 0 0 1 -.883 .993l-.117 .007z" /></svg>{% else %}{{ plan.ram.rstrip('g') }}<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-badge-sd"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M3 5m0 2a2 2 0 0 1 2 -2h14a2 2 0 0 1 2 2v10a2 2 0 0 1 -2 2h-14a2 2 0 0 1 -2 -2z" /><path d="M14 9v6h1a2 2 0 0 0 2 -2v-2a2 2 0 0 0 -2 -2h-1z" /><path d="M7 14.25c0 .414 .336 .75 .75 .75h1.25a1 1 0 0 0 1 -1v-1a1 1 0 0 0 -1 -1h-1a1 1 0 0 1 -1 -1v-1a1 1 0 0 1 1 -1h1.25a.75 .75 0 0 1 .75 .75" /></svg>{% endif %}</td>
<td class="sort-port">{% if plan.bandwidth == 0 %}∞{% else %}{{ plan.bandwidth }} mbits{% endif %}</td>
<td class="">
<div class="btn-list flex-nowrap">
<a href="#modal-edit-plan" data-bs-toggle="modal" data-bs-target="#modal-edit-plan" class="btn-edit btn">
<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-edit" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M7 7h-1a2 2 0 0 0 -2 2v9a2 2 0 0 0 2 2h9a2 2 0 0 0 2 -2v-1" /><path d="M20.385 6.585a2.1 2.1 0 0 0 -2.97 -2.97l-8.415 8.385v3h3l8.385 -8.415z" /><path d="M16 5l3 3" /></svg> Edit
</a>
<span data-bs-toggle="tooltip" data-bs-placement="top" title="Delete plan">
<a href="/plans/delete/{{plan.name}}" class="btn btn-danger" id="deleteButton-{{plan.id}}" data-bs-toggle="modal" data-bs-target="#confirmDeleteUserModal" data-plan-name="{{plan.name}}" data-plan-id="{{plan.id}}">
<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-trash" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
<path d="M4 7l16 0" />
<path d="M10 11l0 6" />
<path d="M14 11l0 6" />
<path d="M5 7l1 12a2 2 0 0 0 2 2h8a2 2 0 0 0 2 -2l1 -12" />
<path d="M9 7v-3a1 1 0 0 1 1 -1h4a1 1 0 0 1 1 1v3" />
</svg> Delete</a>
</span>
</div>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
</div>
{% else %}
<div class="page page-center">
<div class="container-tight py-4">
<div class="empty">
<div class="empty-header">No Plans</div>
<p class="empty-title">No plans or the MySQL service is not running.</p>
<p class="d-none empty-subtitle text-secondary">
</p>
<div class="empty-action">
<button
id="addPlanButton"
class="btn btn-primary"
type="button"
{%
if
images
%}
data-bs-toggle="modal"
data-bs-target="#modal-report"
{%
else
%}
data-bs-toggle="tooltip"
data-bs-placement="bottom"
aria-label="Please ensure that Docker images are added before creating plans."
data-bs-original-title="Please ensure that Docker images are added before creating plans."
{%
endif
%}
>
<svg
xmlns="http://www.w3.org/2000/svg"
class="icon"
width="24"
height="24"
viewBox="0 0 24 24"
stroke-width="2"
stroke="currentColor"
fill="none"
stroke-linecap="round"
stroke-linejoin="round"
>
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
<path d="M12 5l0 14"></path>
<path d="M5 12l14 0"></path></svg
>Create your first plan
</button>
</div>
</div>
</div>
</div>
{% endif %}
</div>
<!-- Delete Modal -->
<div class="modal modal-blur fade" id="confirmDeleteUserModal" aria-labelledby="confirmDeleteUserModalLabel" tabindex="-1" role="dialog" aria-hidden="true" data-bs-backdrop="static">
<div class="modal-dialog modal-dialog-centered" role="document">
<div class="modal-content">
<button type="button" id="deleteModalxClose" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
<div class="modal-status bg-danger"></div>
<div class="modal-body text-center py-4">
<!-- Download SVG icon from http://tabler-icons.io/i/alert-triangle -->
<svg xmlns="http://www.w3.org/2000/svg" class="icon mb-2 text-danger icon-lg" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M10.24 3.957l-8.422 14.06a1.989 1.989 0 0 0 1.7 2.983h16.845a1.989 1.989 0 0 0 1.7 -2.983l-8.423 -14.06a1.989 1.989 0 0 0 -3.4 0z" /><path d="M12 9v4" /><path d="M12 17h.01" /></svg>
<h3 id="confirmDeleteUserModalLabel">Are you sure you want to delete plan with the name: <b><span id="nameofplan"></span></b>?</h3>
<div class="text-muted"> <small>Deleting a plan will remove the network, delete docker image and all settings associated with the plan. Plan can not be deleted if it has active users.
</small></div>
<!-- Input field for confirmation -->
<div class="mt-3 mb-3">
<label for="deleteConfirmation" class="form-label">Type "DELETE" to confirm:</label>
<input type="text" class="form-control" id="deleteConfirmation">
</div>
</div>
<div class="modal-footer">
<div class="w-100">
<div class="row">
<div class="col"><a href="#" id="confirmDelete" class="btn btn-danger w-100" style="display: none;">
Terminate
</a></div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div
class="modal modal-blur fade"
id="modal-edit-plan"
tabindex="-1"
style="display: none"
aria-hidden="true"
>
<div class="modal-dialog modal-lg modal-dialog-centered" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Edit Plan</h5>
<a style="display: none" href="#"
><svg
xmlns="http://www.w3.org/2000/svg"
class="icon icon-tabler icon-tabler-info-square-rounded-filled"
width="24"
height="24"
viewBox="0 0 24 24"
stroke-width="2"
stroke="currentColor"
fill="none"
stroke-linecap="round"
stroke-linejoin="round"
>
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
<path
d="M12 2l.642 .005l.616 .017l.299 .013l.579 .034l.553 .046c4.687 .455 6.65 2.333 7.166 6.906l.03 .29l.046 .553l.041 .727l.006 .15l.017 .617l.005 .642l-.005 .642l-.017 .616l-.013 .299l-.034 .579l-.046 .553c-.455 4.687 -2.333 6.65 -6.906 7.166l-.29 .03l-.553 .046l-.727 .041l-.15 .006l-.617 .017l-.642 .005l-.642 -.005l-.616 -.017l-.299 -.013l-.579 -.034l-.553 -.046c-4.687 -.455 -6.65 -2.333 -7.166 -6.906l-.03 -.29l-.046 -.553l-.041 -.727l-.006 -.15l-.017 -.617l-.004 -.318v-.648l.004 -.318l.017 -.616l.013 -.299l.034 -.579l.046 -.553c.455 -4.687 2.333 -6.65 6.906 -7.166l.29 -.03l.553 -.046l.727 -.041l.15 -.006l.617 -.017c.21 -.003 .424 -.005 .642 -.005zm0 9h-1l-.117 .007a1 1 0 0 0 0 1.986l.117 .007v3l.007 .117a1 1 0 0 0 .876 .876l.117 .007h1l.117 -.007a1 1 0 0 0 .876 -.876l.007 -.117l-.007 -.117a1 1 0 0 0 -.764 -.857l-.112 -.02l-.117 -.006v-3l-.007 -.117a1 1 0 0 0 -.876 -.876l-.117 -.007zm.01 -3l-.127 .007a1 1 0 0 0 0 1.986l.117 .007l.127 -.007a1 1 0 0 0 0 -1.986l-.117 -.007z"
stroke-width="0"
fill="currentColor"
/>
</svg>
</a>
<button
type="button"
class="btn-close"
data-bs-dismiss="modal"
aria-label="Close"
></button>
</div>
<form id="EditplanForm">
<input id="edit_id" class="form-control d-none" name="id" type="number" required="" data-validate value="" />
<div class="modal-body">
<div class="form-selectgroup-boxes row mb-3">
<div class="col-lg-4">
<label class="form-label"
>Name: <span class="text-danger">*</span></label
>
<div class="row g-2">
<div class="col">
<input
id="edit_name"
class="form-control"
name="name"
placeholder="Enter plan name"
type="text"
required=""
data-validate
value=""
/>
</div>
<div class="col-auto align-self-center">
<span
class="form-help"
data-bs-toggle="popover"
data-bs-placement="top"
data-bs-content="<p class='mb-0'>Plan name is visible to the users.</p>"
data-bs-html="true"
>?</span
>
</div>
</div>
</div>
<!-- col -->
<div class="col-lg-8">
<label class="form-label">Description:</label>
<div class="row g-2">
<div class="col">
<input
id="edit_description"
class="form-control"
name="description"
placeholder="Enter description"
type="textarea"
/>
</div>
<div class="col-auto align-self-center">
<span
class="form-help"
data-bs-toggle="popover"
data-bs-placement="top"
data-bs-content="<p class='mb-0'>Description is only visible to Admin users.</p>"
data-bs-html="true"
>?</span
>
</div>
</div>
</div>
</div>
<hr />
<div class="row">
<div class="col-lg-3">
<div class="mb-3">
<label class="form-label"
>Domains: <span class="text-danger">*</span></label
>
<div class="row g-2">
<div class="col">
<input
id="edit_domains_limit"
class="form-control"
name="domains_limit"
placeholder=""
type="number"
required=""
min="0"
data-validate
/>
</div>
<div class="col-auto align-self-center">
<span
class="form-help"
data-bs-toggle="popover"
data-bs-placement="top"
data-bs-content="<p>Total number of domains users are allowed to add on this plan.</p><p class='mb-0'>Subdomains are not included in this limit.</p>"
data-bs-html="true"
>?</span
>
</div>
</div>
</div>
</div>
<div class="col-lg-3">
<div class="mb-3">
<label class="form-label"
>Websites: <span class="text-danger">*</span></label
>
<div class="row g-2">
<div class="col">
<input
id="edit_websites_limit"
class="form-control"
name="websites_limit"
placeholder=""
type="number"
required=""
min="0"
data-validate
/>
</div>
<div class="col-auto align-self-center">
<span
class="form-help"
data-bs-toggle="popover"
data-bs-placement="top"
data-bs-content="<p>Total number of websites users are allowed to manage on this plan.</p><p class='mb-0'>This encompasses both WordPress sites managed via the WP Manager interface and Python/NodeJS applications administered through the PM2 interface.</p>"
data-bs-html="true"
>?</span
>
</div>
</div>
</div>
</div>
<div class="col-lg-3">
<div class="mb-3">
<label class="form-label"
>Databases: <span class="text-danger">*</span></label
>
<div class="row g-2">
<div class="col">
<input
id="edit_db_limit"
class="form-control"
name="edit_db_limit"
placeholder=""
type="number"
required=""
min="0"
data-validate
/>
</div>
<div class="col-auto align-self-center">
<span
class="form-help"
data-bs-toggle="popover"
data-bs-placement="top"
data-bs-content="<p class='mb-0'>Total number of MySQL databases users are allowed to create on this plan.</p>"
data-bs-html="true"
>?</span
>
</div>
</div>
</div>
</div>
<div class="col-lg-3">
<div class="mb-3">
<label class="form-label"
>Emails: <span class="text-danger">*</span></label
>
<div class="row g-2">
<div class="col">
<input
id="edit_email_limit"
class="form-control"
name="edit_email_limit"
placeholder=""
type="number"
required=""
min="0"
data-validate
/>
</div>
<div class="col-auto align-self-center">
<span
class="form-help"
data-bs-toggle="popover"
data-bs-placement="top"
data-bs-content="<p class='mb-0'>Total number of Email accounts users are allowed to create on this plan.</p>"
data-bs-html="true"
>?</span
>
</div>
</div>
</div>
</div>
<div class="col-lg-3">
<div class="mb-3">
<label class="form-label"
>FTP: <span class="text-danger">*</span></label
>
<div class="row g-2">
<div class="col">
<input
id="edit_ftp_limit"
class="form-control"
name="edit_ftp_limit"
placeholder=""
type="number"
required=""
min="0"
data-validate
/>
</div>
<div class="col-auto align-self-center">
<span
class="form-help"
data-bs-toggle="popover"
data-bs-placement="top"
data-bs-content="<p class='mb-0'>Total number of FTP sub-accounts users are allowed to create on this plan.</p>"
data-bs-html="true"
>?</span
>
</div>
</div>
</div>
</div>
<div class="col-lg-3">
<div class="mb-3">
<label class="form-label"
>Memory: <span class="text-danger">*</span></label
>
<div class="row g-2">
<div class="col">
<div class="input-group">
<input
id="edit_ram"
class="form-control"
name="edit_ram"
placeholder=""
type="number"
required=""
min="0"
data-validate
/>
<div class="input-group-append">
<span class="input-group-text" id="ram-gb">GB</span>
</div>
</div>
</div>
<div class="col-auto align-self-center">
<span
class="form-help"
data-bs-toggle="popover"
data-bs-placement="top"
data-bs-content="<p class='mb-0'>Physical Memory (RAM) limit in GB.</p>"
data-bs-html="true"
>?</span
>
</div>
</div>
</div>
</div>
<div class="col-lg-3">
<div class="mb-3">
<label class="form-label"
>CPU: <span class="text-danger">*</span></label
>
<div class="row g-2">
<div class="col">
<div class="input-group">
<input
id="edit_cpu"
class="form-control"
name="edit_cpu"
placeholder=""
type="number"
required=""
min="0"
data-validate
/>
<div class="input-group-append">
<span class="input-group-text" id="cpu-cores"
>cores</span
>
</div>
</div>
</div>
<div class="col-auto align-self-center">
<span
class="form-help"
data-bs-toggle="popover"
data-bs-placement="top"
data-bs-content="<p class='mb-0'>Number of CPU cores.</p>"
data-bs-html="true"
>?</span
>
</div>
</div>
</div>
</div>
<div class="col-lg-3">
<div class="mb-3">
<label class="form-label"
>Port Speed: <span class="text-danger">*</span></label
>
<div class="row g-2">
<div class="col">
<div class="input-group">
<input
id="edit_port_speed"
class="form-control"
name="edit_port_speed"
placeholder=""
type="number"
name="port_speed"
required=""
min="1"
data-validate
/>
<div class="input-group-append">
<span class="input-group-text" id="port-mbits"
>mbits</span
>
</div>
</div>
</div>
<div class="col-auto align-self-center">
<span
class="form-help"
data-bs-toggle="popover"
data-bs-placement="top"
data-bs-content="<p class='mb-0'>Port Speed in mbit/s (i.e., the network speed or bandwidth available to the user for network operations).</p>"
data-bs-html="true"
>?</span
>
</div>
</div>
</div>
</div>
<div class="col-lg-6">
<div class="mb-3">
<label class="form-label"
>Docker image: <span class="text-danger">*</span></label
>
<div class="row g-2">
<div class="col">
<input
id="edit_docker_image"
class="form-control"
name="edit_docker_image"
type="text"
required=""
disabled
>
</div>
<div class="col-auto align-self-center">
<span
class="form-help"
data-bs-toggle="popover"
data-bs-placement="top"
data-bs-content="<p>Docker image can not be changed, as it is not possible to switch databases from MySQL to MariaDB or webserver from Nginx to Apache.</p>"
data-bs-html="true"
>?</span
>
</div>
</div>
</div>
</div>
<div class="col-lg-6">
<div class="mb-3">
<label class="form-label"
>Container size: <span class="text-danger">*</span></label
>
<div class="row g-2">
<div class="col">
<div class="input-group">
<input
id="edit_disk_limit"
class="form-control"
name="edit_disk_limit"
placeholder="10GB minimum"
type="number"
required=""
min="10"
data-validate
/>
<div class="input-group-append">
<span class="input-group-text" id="disk-gb">GB</span>
</div>
</div>
</div>
<div class="col-auto align-self-center">
<span
class="form-help"
data-bs-toggle="popover"
data-bs-placement="top"
data-bs-content="<p>Total disk usage limit in GB for the user Docker container.</p><p class='mb-0'>This limit encompasses all user data <b>with the exception of the home directory</b>. This includes all system files, services, logs, MySQL databases, etc., Docker requires that the limit must be at least 10GB.</p>"
data-bs-html="true"
>?</span
>
</div>
</div>
</div>
</div>
<div class="col-lg-6">
<div class="mb-3">
<label class="form-label"
>Inodes limit: <span class="text-danger">*</span></label
>
<div class="row g-2">
<div class="col">
<input
id="edit_storage_file_inodes"
class="form-control"
name="edit_storage_file_inodes"
placeholder="minimum 500k"
type="number"
required=""
min="500000"
data-validate
/>
</div>
<div class="col-auto align-self-center">
<span
class="form-help"
data-bs-toggle="popover"
data-bs-placement="top"
data-bs-content="<p>Total number of inodes for the users home directory.</p><p class='mb-0'>500.000 is the minimum.</p>"
data-bs-html="true"
>?</span
>
</div>
</div>
</div>
</div>
<div class="col-lg-6">
<div class="mb-3">
<label class="form-label"
>Storage file: <span class="text-danger">*</span></label
>
<div class="row g-2">
<div class="col">
<div class="input-group">
<input
id="edit_storage_file"
class="form-control"
name="edit_storage_file"
placeholder=""
type="number"
required=""
min="0"
data-validate
/>
<div class="input-group-append">
<span class="input-group-text" id="disk-gb">GB</span>
</div>
</div>
</div>
<div class="col-auto align-self-center">
<span
class="form-help"
data-bs-toggle="popover"
data-bs-placement="top"
data-bs-content="<p>The maximum disk space allocation for the user's home directory in gigabytes.</p><p class='mb-0'>Enter 0 to allow unlimited disk space (recommended only for Virtual Private Servers operated by a single user).</p>"
data-bs-html="true"
>?</span
>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="modal-footer">
<a
href="#"
class="btn btn-link link-secondary"
data-bs-dismiss="modal"
>
Cancel
</a>
<button
type="button"
class="btn btn-primary ms-auto"
id="SavePlanButton"
>
Save Plan
</button>
</div>
</form>
</div>
<script>
$(document).ready(function() {
// Attach click event listener to "Edit" buttons
$('.btn-edit').click(function() {
// Get the row corresponding to the clicked "Edit" button
var row = $(this).closest('tr');
// Extract data from the row
var id = row.data('id');
var name = row.data('name');
var description = row.data('description');
var dockerImage = row.data('image');
var diskLimitReal = row.find('.sort-du').text();
var diskLimit = (diskLimitReal.trim() === '∞') ? '0' : diskLimitReal.replace(' GB', '');
var storageFileText = row.find('.sort-storage').text();
var storageFileInodesText = row.find('.sort-storage-inodes').text();
var storageFile = (storageFileText.trim() === '∞') ? '0' : storageFileText.replace(' GB', '');
var domainsLimitReal = row.find('.sort-domains').text();
var domainsLimit = (domainsLimitReal.trim() === '∞') ? '0' : domainsLimitReal;
var websitesLimitReal = row.find('.sort-sites').text();
var websitesLimit = (websitesLimitReal.trim() === '∞') ? '0' : websitesLimitReal;
var ftpLimitReal = row.find('.sort-ftp').text();
var ftpLimit = (ftpLimitReal.trim() === '∞') ? '0' : ftpLimitReal;
var emailsLimitReal = row.find('.sort-emails').text();
var emailsLimit = (emailsLimitReal.trim() === '∞') ? '0' : emailsLimitReal;
var dbLimitReal = row.find('.sort-db').text();
var dbLimit = (dbLimitReal.trim() === '∞') ? '0' : dbLimitReal;
var cpuReal = row.find('.sort-cpu').text();
var cpu = (cpuReal.trim() === '∞') ? '0' : cpuReal;
var ramReal = row.find('.sort-ram').text();
var ram = (ramReal.trim() === '∞') ? '0' : ramReal;
var portSpeedReal = row.find('.sort-port').text();
var portSpeed = (portSpeedReal.trim() === '∞') ? '10000' : portSpeedReal.replace(' mbits', '');
// Populate modal fields with extracted data
$('#edit_id').val(id);
$('#edit_name').val(name);
$('#edit_description').val(description);
$('#edit_docker_image').val(dockerImage);
$('#edit_disk_limit').val(diskLimit);
$('#edit_storage_file').val(storageFile);
$('#edit_storage_file_inodes').val(storageFileInodesText);
$('#edit_domains_limit').val(domainsLimit);
$('#edit_websites_limit').val(websitesLimit);
$('#edit_email_limit').val(emailsLimit);
$('#edit_ftp_limit').val(ftpLimit);
$('#edit_db_limit').val(dbLimit);
$('#edit_cpu').val(cpu);
$('#edit_ram').val(ram);
$('#edit_port_speed').val(portSpeed);
// Show the modal
//$('#modal-edit-plan').modal('show');
});
});
</script>
<script>
// Function to handle edit plan form submission
function submitForm() {
// Serialize form data
const formData = new FormData(document.getElementById('EditplanForm'));
const url = '/plan/edit';
// Make POST request
fetch(url, {
method: 'POST',
body: formData
})
.then(response => {
if (response.ok) {
// Extract success message
response.json().then(data => {
const success = data.success || false;
let responseMessage = success ? data.response.message : '';
responseMessage = responseMessage.replace(/\n/g, '<br>');
const modalBody = document.querySelector('#modal-edit-plan .modal-body');
//const modalHeader = document.querySelector('#modal-edit-plan .modal-header');
const modalFooter = document.querySelector('#modal-edit-plan .modal-footer');
const modalContent = document.querySelector('#modal-edit-plan .modal-content');
// Extract filename if "opencli_plan_apply_" is present
const filenameMatch = responseMessage.match(/opencli_plan_apply_\d{8}_\d{6}\.log/);
if (filenameMatch) {
const filename = filenameMatch[0];
const preTag = document.createElement('pre');
preTag.style.height = '30em';
const successParagraph = document.createElement('p');
successParagraph.innerHTML = responseMessage;
successParagraph.innerHTML = successParagraph.innerHTML.replace(/(tail -f \/tmp\/opencli_plan_apply_\d{8}_\d{6}\.log)/g, '<code>$1</code>');
// Function to fetch data from /plan/apply/<filename> and update pre tag
const fetchDataAndUpdatePreTag = () => {
fetch(`/plan/apply/${filename}`)
.then(response => response.text())
.then(data => {
preTag.textContent = data;
modalFooter.innerHTML = '';
modalBody.innerHTML = '';
modalBody.appendChild(successParagraph);
modalBody.appendChild(preTag);
if (data.includes('COMPLETED')) {
clearInterval(intervalId);
setTimeout(() => {
window.location.reload();
}, 1500);
}
})
.catch(error => {
console.error('Error fetching data:', error);
});
};
// Fetch data initially
fetchDataAndUpdatePreTag();
// Refresh every 1 second
const intervalId = setInterval(fetchDataAndUpdatePreTag, 1000);
} else {
// If filenameMatch is not found, display success alert
const alertDiv = document.createElement('div');
if (responseMessage.includes('ERROR')) {
alertDiv.className = 'alert alert-danger';
} else {
alertDiv.className = 'alert alert-success';
}
alertDiv.innerHTML = responseMessage;
modalContent.insertBefore(alertDiv, modalContent.children[1]);
// Hide alerts after 5 seconds
setTimeout(() => {
alertDiv.style.display = 'none';
}, 5000);
}
});
} else {
// Handle error response
response.json().then(data => {
const errorMessage = data.error || 'An error occurred while processing your request.';
// Create a new div for alert
const alertDiv = document.createElement('div');
alertDiv.className = 'alert alert-danger';
alertDiv.innerHTML = errorMessage;
// Insert the new div after modal header
modalContent.insertBefore(alertDiv, modalContent.children[1]); // Assuming the second child is modal-body
// Hide alerts after 5 seconds
setTimeout(() => {
alertDiv.style.display = 'none';
}, 5000);
});
}
})
.catch(error => {
console.error('Error:', error);
const modalContent = document.querySelector('#modal-edit-plan .modal-content'); // Targeting modal content
// Create a new div for alert
const alertDiv = document.createElement('div');
alertDiv.className = 'alert alert-danger';
alertDiv.innerHTML = 'An error occurred while processing your request.';
// Insert the new div after modal header
modalContent.insertBefore(alertDiv, modalContent.children[1]); // Assuming the second child is modal-body
// Hide alerts after 5 seconds
setTimeout(() => {
alertDiv.style.display = 'none';
}, 5000);
});
}
$(document).ready(function() {
$('#SavePlanButton').click(function() {
submitForm();
});
});
document.addEventListener('DOMContentLoaded', function() {
// Function to check URL hash and add class to the matching row
function applyClassBasedOnHash() {
// Get the hash from the URL (e.g., #ubuntu_nginx_mysql)
const hash = window.location.hash.substring(1); // Remove the # symbol
// If there's a hash value, find the matching <tr> by data-name attribute
if (hash) {
// Remove 'this-user-row' class from all rows first (optional, depending on your logic)
document.querySelectorAll('tr.this-user-row').forEach(function(row) {
row.classList.remove('this-user-row');
});
// Find the row with the matching data-name
const matchingRow = document.querySelector(`tr[data-name="${hash}"]`);
// If the matching row is found, add the class
if (matchingRow) {
matchingRow.classList.add('this-user-row');
}
}
}
// Run the function on page load (if there's already a hash in the URL)
applyClassBasedOnHash();
// Listen for hash changes in the URL
window.addEventListener('hashchange', applyClassBasedOnHash);
});
</script>
<style>
tr.this-user-row {
border-left: .25rem var(--tblr-border-style) blue;
}
</style>
<script src="{{ url_for('static', filename='pages/plans.js') }}" defer></script>
{% endblock %}