mirror of
https://github.com/bunkerity/bunkerweb
synced 2026-05-24 09:28:37 +00:00
Add is_pro column to metadata and change UI back logic with pro plugins
This commit is contained in:
parent
b491a85e46
commit
6d08031054
14 changed files with 81 additions and 107 deletions
File diff suppressed because one or more lines are too long
|
Before Width: | Height: | Size: 905 KiB After Width: | Height: | Size: 905 KiB |
|
|
@ -34,7 +34,7 @@ from Database import Database # type: ignore
|
|||
from logger import setup_logger # type: ignore
|
||||
from jobs import get_os_info, get_integration, get_version # type: ignore
|
||||
|
||||
API_ENDPOINT = "https://api.bunkerweb.io/pro"
|
||||
API_ENDPOINT = "http://api:8080/pro"
|
||||
TMP_DIR = Path(sep, "var", "tmp", "bunkerweb", "pro", "plugins")
|
||||
PRO_PLUGINS_DIR = Path(sep, "etc", "bunkerweb", "pro", "plugins")
|
||||
logger = setup_logger("Jobs.download-pro-plugins", getenv("LOG_LEVEL", "INFO"))
|
||||
|
|
@ -94,12 +94,15 @@ try:
|
|||
status = 2
|
||||
sys_exit(status)
|
||||
|
||||
db = Database(logger, sqlalchemy_string=getenv("DATABASE_URI"), pool=False)
|
||||
temp_dir = TMP_DIR.joinpath(str(uuid4()))
|
||||
temp_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
if resp.headers.get("Content-Type") == "application/zip":
|
||||
logger.info("🚀 Your BunkerWeb Pro license is valid, checking if there are new or updated pro plugins...")
|
||||
|
||||
db.set_is_pro(True)
|
||||
|
||||
with BytesIO(resp.content) as plugin_content:
|
||||
with ZipFile(plugin_content) as zf:
|
||||
zf.extractall(path=temp_dir)
|
||||
|
|
@ -109,13 +112,14 @@ try:
|
|||
message = "Your BunkerWeb Pro license is not valid or has expired"
|
||||
logger.warning(f"{message}, only checking if there are new or updated info about pro plugins...")
|
||||
|
||||
db.set_is_pro(False)
|
||||
|
||||
plugins = resp.json()
|
||||
for plugin in plugins["data"]:
|
||||
plugin_path = temp_dir.joinpath(plugin["id"])
|
||||
plugin_path.mkdir(parents=True, exist_ok=True)
|
||||
plugin_path.joinpath("plugin.json").write_text(dumps(plugin, indent=4), encoding="utf-8")
|
||||
|
||||
db = Database(logger, sqlalchemy_string=getenv("DATABASE_URI"), pool=False)
|
||||
plugin_nbr = 0
|
||||
|
||||
# Install plugins
|
||||
|
|
|
|||
|
|
@ -223,6 +223,22 @@ class Database:
|
|||
|
||||
return ""
|
||||
|
||||
def set_is_pro(self, value: bool = False) -> str:
|
||||
"""Set the is_pro value"""
|
||||
with self.__db_session() as session:
|
||||
try:
|
||||
metadata = session.query(Metadata).get(1)
|
||||
|
||||
if not metadata:
|
||||
return "The metadata are not set yet, try again"
|
||||
|
||||
metadata.is_pro = value
|
||||
session.commit()
|
||||
except BaseException:
|
||||
return format_exc()
|
||||
|
||||
return ""
|
||||
|
||||
def is_scheduler_first_start(self) -> bool:
|
||||
"""Check if it's the scheduler's first start"""
|
||||
with self.__db_session() as session:
|
||||
|
|
@ -274,14 +290,14 @@ class Database:
|
|||
|
||||
def get_metadata(self) -> Dict[str, str]:
|
||||
"""Get the metadata from the database"""
|
||||
data = {"version": "1.5.6", "integration": "unknown", "database_version": "Unknown"}
|
||||
data = {"version": "1.5.6", "integration": "unknown", "database_version": "Unknown", "is_pro": False}
|
||||
database = self.database_uri.split(":")[0].split("+")[0]
|
||||
with self.__db_session() as session:
|
||||
with suppress(ProgrammingError, OperationalError):
|
||||
data["database_version"] = (session.execute(text("SELECT sqlite_version()" if database == "sqlite" else "SELECT VERSION()")).first() or ["unknown"])[0]
|
||||
metadata = session.query(Metadata).with_entities(Metadata.version, Metadata.integration).filter_by(id=1).first()
|
||||
metadata = session.query(Metadata).with_entities(Metadata.version, Metadata.integration, Metadata.is_pro).filter_by(id=1).first()
|
||||
if metadata:
|
||||
data.update({"version": metadata.version, "integration": metadata.integration})
|
||||
data.update({"version": metadata.version, "integration": metadata.integration, "is_pro": metadata.is_pro})
|
||||
|
||||
return data
|
||||
|
||||
|
|
|
|||
|
|
@ -269,6 +269,7 @@ class Metadata(Base):
|
|||
|
||||
id = Column(Integer, primary_key=True, default=1)
|
||||
is_initialized = Column(Boolean, nullable=False)
|
||||
is_pro = Column(Boolean, default=False, nullable=False)
|
||||
first_config_saved = Column(Boolean, nullable=False)
|
||||
autoconf_loaded = Column(Boolean, default=False, nullable=True)
|
||||
scheduler_first_start = Column(Boolean, nullable=True)
|
||||
|
|
|
|||
|
|
@ -55,11 +55,6 @@ from Database import Database # type: ignore
|
|||
from logging import getLogger
|
||||
|
||||
|
||||
# REPLACE BY REAL VALUES AFTER
|
||||
PRO_VERSION = False
|
||||
PRO_PLUGINS_LIST = [{"name": "metrics pro", "id": "metricspro", "type": "pro"}, {"name": "prometheus", "id": "prometheus", "type": "pro"}, {"name": "emergency", "id": "emergency", "type": "pro"}]
|
||||
|
||||
|
||||
def stop_gunicorn():
|
||||
p = Popen(["pgrep", "-f", "gunicorn"], stdout=PIPE)
|
||||
out, _ = p.communicate()
|
||||
|
|
@ -338,8 +333,8 @@ def inject_variables():
|
|||
return dict(
|
||||
dark_mode=app.config["DARK_MODE"],
|
||||
script_nonce=app.config["SCRIPT_NONCE"],
|
||||
is_pro_version=PRO_VERSION,
|
||||
plugins_pro=PRO_PLUGINS_LIST,
|
||||
is_pro_version=db.get_metadata()["is_pro"],
|
||||
plugins=app.config["CONFIG"].get_plugins(),
|
||||
)
|
||||
|
||||
|
||||
|
|
@ -834,7 +829,6 @@ def services():
|
|||
}
|
||||
for service in services
|
||||
],
|
||||
plugins=app.config["CONFIG"].get_plugins(),
|
||||
global_config=app.config["CONFIG"].get_config(),
|
||||
username=current_user.get_id(),
|
||||
)
|
||||
|
|
@ -895,7 +889,6 @@ def global_config():
|
|||
return render_template(
|
||||
"global_config.html",
|
||||
username=current_user.get_id(),
|
||||
plugins=app.config["CONFIG"].get_plugins(),
|
||||
global_config=app.config["CONFIG"].get_config(),
|
||||
)
|
||||
|
||||
|
|
@ -1236,7 +1229,6 @@ def plugins():
|
|||
|
||||
return render_template(
|
||||
"plugins.html",
|
||||
plugins=plugins,
|
||||
plugins_count_internal=plugins_internal,
|
||||
plugins_count_external=plugins_external,
|
||||
plugins_count_pro=plugins_pro,
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
3
src/ui/static/js/utils/purify/purify.min.js
vendored
3
src/ui/static/js/utils/purify/purify.min.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
3
src/ui/templates/footer.html
vendored
3
src/ui/templates/footer.html
vendored
|
|
@ -35,7 +35,8 @@
|
|||
<li class="nav-item">
|
||||
<a href="{{ item['link'] }}"
|
||||
class="hover:italic hover:brightness-90 block sm:px-4 pt-1 pb-0 lg:pb-1 text-xs tracking-wide font-normal transition duration-300 ease-in-out text-white dark:text-white"
|
||||
target="_blank">{{ item['name'] }}</a>
|
||||
target="_blank"
|
||||
rel="noopener">{{ item['name'] }}</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
|
|
|
|||
2
src/ui/templates/home.html
vendored
2
src/ui/templates/home.html
vendored
|
|
@ -10,7 +10,7 @@
|
|||
<div class="col-span-12 grid grid-cols-12 justify-start items-start gap-4">
|
||||
{% for card in cards %}
|
||||
<!-- stats card -->
|
||||
<a href="{{ card['link'] }}" class="home-card" target="_blank">
|
||||
<a href="{{ card['link'] }}" class="home-card" target="_blank" rel="noopener">
|
||||
<!-- text -->
|
||||
<div>
|
||||
<p class="home-card-name">{{ card['name'] }}</p>
|
||||
|
|
|
|||
85
src/ui/templates/menu.html
vendored
85
src/ui/templates/menu.html
vendored
|
|
@ -198,53 +198,32 @@
|
|||
</ul>
|
||||
<!-- end default anchor -->
|
||||
<!-- plugin list -->
|
||||
{% set isPage = {'value': False} %} {% set plugins = config["CONFIG"].get_plugins() %}
|
||||
{% for plugin in plugins %}
|
||||
{% if
|
||||
plugin['page'] %}
|
||||
{% if isPage.update({"value": True}) %}{% endif %}
|
||||
{%
|
||||
endif %}
|
||||
{% endfor %}
|
||||
<div>
|
||||
<ul>
|
||||
{% if isPage.value == True %}
|
||||
<li class="w-full mt-4">
|
||||
<h6 class="pl-6 ml-2 text-xs font-bold leading-tight uppercase dark:text-white opacity-60">PLUGINS PAGE</h6>
|
||||
</li>
|
||||
{% else %}
|
||||
<li class="w-full mt-8">
|
||||
<h6 class="text-center pl-6 pr-8 ml-2 text-xs leading-tight uppercase text-gray-600/90 dark:text-white/80">
|
||||
Want your own plugins ?
|
||||
<br />
|
||||
<a class="leading-8 font-bold hover:brightness-75"
|
||||
target="_blank"
|
||||
href="https://docs.bunkerweb.io/latest/plugins/?utm_campaign=self&utm_source=ui#writing-a-plugin">check doc</a>
|
||||
</h6>
|
||||
<div>
|
||||
<ul>
|
||||
<li class="w-full mt-4">
|
||||
<h6 class="pl-6 ml-2 text-xs font-bold leading-tight uppercase dark:text-white opacity-60">PLUGINS PAGE</h6>
|
||||
</li>
|
||||
{% for plugin in plugins %}
|
||||
{% if plugin['page'] and plugin['type'] != "pro" %}
|
||||
<li class="mt-0.5 w-full">
|
||||
<a class="dark:hover:bg-primary/20 hover:bg-primary/5 hover:rounded-lg dark:text-white dark:opacity-80 py-1 text-sm ease-nav-brand my-0 mx-2 flex items-center whitespace-nowrap px-4 transition"
|
||||
href="{{ request.url_root }}plugins/{{ plugin['id'] }}">
|
||||
<div class="mr-2 flex items-center justify-center rounded-lg bg-center stroke-0 text-center p-1 xl:p-1.5">
|
||||
<svg class="fill-gray-500 h-5 w-5 relative"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 384 512">
|
||||
<path d="M0 64C0 28.7 28.7 0 64 0H224V128c0 17.7 14.3 32 32 32H384V448c0 35.3-28.7 64-64 64H64c-35.3 0-64-28.7-64-64V64zm384 64H256V0L384 128z" />
|
||||
</svg>
|
||||
</div>
|
||||
<span class="ml-1 duration-300 opacity-100 pointer-events-none ease">{{ plugin['name'] }}</span>
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% for plugin in plugins %}
|
||||
{% if plugin['page'] %}
|
||||
<li class="mt-0.5 w-full">
|
||||
<a class="dark:hover:bg-primary/20 hover:bg-primary/5 hover:rounded-lg dark:text-white dark:opacity-80 py-1 text-sm ease-nav-brand my-0 mx-2 flex items-center whitespace-nowrap px-4 transition"
|
||||
href="{{ request.url_root }}plugins/{{ plugin['id'] }}">
|
||||
<div class="mr-2 flex items-center justify-center rounded-lg bg-center stroke-0 text-center p-1 xl:p-1.5">
|
||||
<svg class="fill-gray-500 h-5 w-5 relative"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 384 512">
|
||||
<path d="M0 64C0 28.7 28.7 0 64 0H224V128c0 17.7 14.3 32 32 32H384V448c0 35.3-28.7 64-64 64H64c-35.3 0-64-28.7-64-64V64zm384 64H256V0L384 128z" />
|
||||
</svg>
|
||||
</div>
|
||||
<span class="ml-1 duration-300 opacity-100 pointer-events-none ease">{{ plugin['name'] }}</span>
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% if plugins_pro %}
|
||||
{% for plugin in plugins_pro %}
|
||||
<a {% if not is_pro_version %}target="_blank"{% endif %}
|
||||
{% if plugin['page'] and plugin['type'] == "pro" %}
|
||||
<li class="mt-0.5 w-full">
|
||||
<a {% if not is_pro_version %}target="_blank" rel="noopener"{% endif %}
|
||||
class="dark:hover:bg-primary/20 hover:bg-primary/5 hover:rounded-lg dark:text-white dark:opacity-80 py-1 text-sm ease-nav-brand my-0 mx-2 flex items-center whitespace-nowrap px-4 transition"
|
||||
href="{% if not is_pro_version %}https://panel.bunkerweb.io/{% else %}{{ request.url_root }}plugins/{{ plugin['id'] }}{% endif %}">
|
||||
href="{% if not is_pro_version %}https://panel.bunkerweb.io/{% else %}javascript:void(0){% endif %}"
|
||||
<div class="mr-2 flex items-center justify-center rounded-lg bg-center stroke-0 text-center p-1 xl:p-1.5">
|
||||
<svg class="h-5 w-5 dark:brightness-90"
|
||||
viewBox="0 0 48 46"
|
||||
|
|
@ -254,11 +233,11 @@
|
|||
<path class="fill-yellow-500" d="M21.2803 45.5H26.7198C33.8098 45.5 37.3545 45.5 39.7198 43.383C40.7523 42.4588 41.4057 40.793 41.8775 38.625H6.1224C6.59413 40.793 7.24783 42.4588 8.2802 43.383C10.6454 45.5 14.1903 45.5 21.2803 45.5Z" fill="#1C274C" />
|
||||
</svg>
|
||||
</div>
|
||||
<span class="ml-1 duration-300 {% if not is_plugin_pro %}opacity-80 dark:opacity-60{% endif %} pointer-events-none ease">{{ plugin['name'] }}</span>
|
||||
<span class="ml-1 duration-300 {% if not is_pro_version %}opacity-80 dark:opacity-60{% endif %} pointer-events-none ease">{{ plugin['name'] }}</span>
|
||||
</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</ul>
|
||||
<!-- end plugin list -->
|
||||
</div>
|
||||
|
|
@ -295,7 +274,8 @@
|
|||
<li class="mx-2 w-6">
|
||||
<a aria-label="link to twitter"
|
||||
href="https://twitter.com/bunkerity"
|
||||
target="_blank">
|
||||
target="_blank"
|
||||
rel="noopener">
|
||||
<svg class="hover:opacity-80"
|
||||
fill="#1DA1F2"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
|
|
@ -307,7 +287,8 @@
|
|||
<li class="mx-2.5 w-6">
|
||||
<a aria-label="link to linkedin"
|
||||
href="https://www.linkedin.com/company/bunkerity/"
|
||||
target="_blank">
|
||||
target="_blank"
|
||||
rel="noopener">
|
||||
<svg fill="#0A63BC"
|
||||
class="hover:opacity-80 dark:brightness-110"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
|
|
@ -319,7 +300,8 @@
|
|||
<li class="mx-2.5 w-6 -translate-y-1">
|
||||
<a aria-label="link to discord"
|
||||
href="https://discord.gg/fTf46FmtyD"
|
||||
target="_blank">
|
||||
target="_blank"
|
||||
rel="noopener">
|
||||
<svg class="translate-y-1 hover:opacity-80"
|
||||
fill="#5562EA"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
|
|
@ -331,7 +313,8 @@
|
|||
<li class="mx-2.5 w-6">
|
||||
<a aria-label="link to github"
|
||||
href="https://github.com/bunkerity"
|
||||
target="_blank">
|
||||
target="_blank"
|
||||
rel="noopener">
|
||||
<svg class="hover:opacity-80 dark:fill-gray-600"
|
||||
fill="#171A1F"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
|
|
|
|||
3
src/ui/templates/news.html
vendored
3
src/ui/templates/news.html
vendored
|
|
@ -92,7 +92,8 @@
|
|||
I've read and agree to the
|
||||
<a class="italic"
|
||||
href="https://www.bunkerity.com/privacy-policy/?utm_campaign=self&utm_source=ui"
|
||||
target="_blank">privacy policy</a>
|
||||
target="_blank"
|
||||
rel="noopener">privacy policy</a>
|
||||
</label>
|
||||
</div>
|
||||
<button type="submit"
|
||||
|
|
|
|||
41
src/ui/templates/plugins.html
vendored
41
src/ui/templates/plugins.html
vendored
|
|
@ -154,13 +154,13 @@
|
|||
<div data-plugins-list class="grid grid-cols-12 gap-3">
|
||||
{% for plugin in plugins %}
|
||||
<div data-plugins-type="{{ plugin['type'] }}"
|
||||
class="py-3 min-h-12 relative col-span-12 sm:col-span-6 2xl:col-span-4 3xl:col-span-3 p-1 flex justify-between items-center transition rounded bg-gray-100 hover:bg-gray-300 dark:bg-slate-700 dark:hover:bg-slate-800">
|
||||
class="py-3 min-h-12 relative col-span-12 sm:col-span-6 2xl:col-span-4 3xl:col-span-3 p-1 flex justify-between items-center transition rounded {% if plugin['type'] != 'pro' or plugin['type'] == 'pro' and is_pro_version %} bg-gray-100 hover:bg-gray-300 dark:bg-slate-700 dark:hover:bg-slate-800 {% else %} bg-gray-300 dark:bg-gray-800 {% endif %}">
|
||||
<p data-plugins-content
|
||||
class="ml-3 mr-2 break-words mb-0 transition duration-300 ease-in-out dark:opacity-90 text-left text-sm md:text-base text-slate-700 dark:text-gray-200">
|
||||
class="{% if plugin['type'] == 'pro' and not is_pro_version %} opacity-80 dark:opacity-60 {% endif %} ml-3 mr-2 break-words mb-0 transition duration-300 ease-in-out dark:opacity-90 text-left text-sm md:text-base text-slate-700 dark:text-gray-200">
|
||||
{{ plugin['name'] }}
|
||||
</p>
|
||||
<div class="flex items-center">
|
||||
{% if plugin['page'] %}
|
||||
{% if plugin['page'] and plugin['type'] != "pro" or (plugin['page'] and plugin['type'] == "pro" and is_pro_version) %}
|
||||
<a aria-label="plugin page link"
|
||||
class="hover:-translate-y-px mx-1"
|
||||
href="{{ request.url_root }}plugins/{{ plugin['id'] }}">
|
||||
|
|
@ -184,34 +184,11 @@
|
|||
</svg>
|
||||
</button>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% if plugins_pro %}
|
||||
{% for plugin in plugins_pro %}
|
||||
<div data-plugins-type="{{ plugin['type'] }}"
|
||||
class="py-3 min-h-12 relative col-span-12 sm:col-span-6 2xl:col-span-4 3xl:col-span-3 p-1 flex justify-between items-center transition rounded {% if is_plugin_pro %}bg-gray-100 hover:bg-gray-300 dark:bg-slate-700 dark:hover:bg-slate-800{% else %} bg-gray-100 dark:bg-slate-700 {% endif %}">
|
||||
<p data-plugins-content
|
||||
class="{% if not is_plugin_pro %} opacity-80 dark:opacity-60{% endif %} ml-3 mr-2 break-words mb-0 transition duration-300 ease-in-out text-left text-sm md:text-base text-slate-700 dark:text-gray-200">
|
||||
{{ plugin['name'] }}
|
||||
</p>
|
||||
<div class="flex items-center">
|
||||
{% if plugin['page'] and is_pro_version %}
|
||||
<a aria-label="plugin page link"
|
||||
class="hover:-translate-y-px mx-1"
|
||||
href="{{ request.url_root }}plugins/{{ plugin['id'] }}">
|
||||
<svg class="h-6 w-6 fill-sky-500 dark dark:brightness-90"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 448 512">
|
||||
<path d="M288 32c-17.7 0-32 14.3-32 32s14.3 32 32 32h50.7L169.4 265.4c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0L384 141.3V192c0 17.7 14.3 32 32 32s32-14.3 32-32V64c0-17.7-14.3-32-32-32H288zM80 64C35.8 64 0 99.8 0 144V400c0 44.2 35.8 80 80 80H336c44.2 0 80-35.8 80-80V320c0-17.7-14.3-32-32-32s-32 14.3-32 32v80c0 8.8-7.2 16-16 16H80c-8.8 0-16-7.2-16-16V144c0-8.8 7.2-16 16-16h80c17.7 0 32-14.3 32-32s-14.3-32-32-32H80z">
|
||||
</path>
|
||||
</svg>
|
||||
</a>
|
||||
{% endif %}
|
||||
<a {% if not is_pro_version %}target="_blank"{% endif %}
|
||||
{% if plugin['type'] == "pro" %}
|
||||
<a {% if not is_pro_version %}target="_blank" rel="noopener"{% endif %}
|
||||
aria-label="plugin page link"
|
||||
class="hover:-translate-y-px mx-1 -translate-y-0.5"
|
||||
href="{% if not is_pro_version %}https://panel.bunkerweb.io/{% else %}#{% endif %}">
|
||||
href="{% if not is_pro_version %}https://panel.bunkerweb.io/{% else %}javascript:void(0){% endif %}">
|
||||
<svg class="h-6 w-6 dark:brightness-90"
|
||||
viewBox="0 0 48 46"
|
||||
fill="none"
|
||||
|
|
@ -220,10 +197,10 @@
|
|||
<path class="fill-yellow-500" d="M21.2803 45.5H26.7198C33.8098 45.5 37.3545 45.5 39.7198 43.383C40.7523 42.4588 41.4057 40.793 41.8775 38.625H6.1224C6.59413 40.793 7.24783 42.4588 8.2802 43.383C10.6454 45.5 14.1903 45.5 21.2803 45.5Z" fill="#1C274C" />
|
||||
</svg>
|
||||
</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
<div data-plugins-nomatch
|
||||
|
|
|
|||
1
src/ui/templates/services.html
vendored
1
src/ui/templates/services.html
vendored
|
|
@ -327,6 +327,7 @@
|
|||
<a aria-label="access service url"
|
||||
href="http://{{ service['SERVER_NAME']['value'] }}"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
class="dark:brightness-90 z-20 mx-1 bg-sky-500 hover:bg-sky-500/80 focus:bg-sky-500/80 inline-block p-3 font-bold text-center text-white uppercase align-middle transition-all rounded-lg cursor-pointer leading-normal text-xs ease-in tracking-tight-rem shadow-xs bg-150 bg-x-25 active:opacity-85 hover:shadow-md">
|
||||
<svg class="h-6 w-6 fill-white"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
|
|
|
|||
Loading…
Reference in a new issue