Merge branch 'dev' of github.com:bunkerity/bunkerweb into dev

This commit is contained in:
florian 2024-01-08 21:07:17 +01:00
commit 6a33859d23
No known key found for this signature in database
GPG key ID: 93EE47CC3D061500
16 changed files with 89 additions and 4239 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

View file

@ -731,7 +731,12 @@ Review your final BunkerWeb UI URL and then click on the `Setup` button. Once th
You can change the username, password needed and manage two-factor authentication by **accessing the account page** of the web UI from the menu.
TODO : insert menu image
You need to click on `manage account` inside the sidebar menu.
<figure markdown>
![Overview](assets/img/manage-account.webp){ align=center, width="350" }
<figcaption>Account page access from menu</figcaption>
</figure>
### Username / Password

View file

@ -531,25 +531,26 @@ def home():
services_scheduler_count=services_scheduler_count,
services_ui_count=services_ui_count,
services_autoconf_count=services_autoconf_count,
username=current_user.get_id(),
dark_mode=app.config["DARK_MODE"],
)
@app.route("/profile", methods=["GET", "POST"])
@app.route("/account", methods=["GET", "POST"])
@login_required
def profile():
def account():
if request.method == "POST":
# Check form data validity
if not request.form:
flash("Missing form data.", "error")
return redirect(url_for("profile"))
return redirect(url_for("account"))
elif "operation" not in request.form:
flash("Missing operation parameter.", "error")
return redirect(url_for("profile"))
return redirect(url_for("account"))
if "curr_password" not in request.form or not current_user.check_password(request.form["curr_password"]):
flash(f"The current password is incorrect. ({request.form['operation']})", "error")
return redirect(url_for("profile"))
return redirect(url_for("account"))
username = current_user.get_id()
password = request.form["curr_password"]
@ -559,10 +560,10 @@ def profile():
if request.form["operation"] == "username":
if "admin_username" not in request.form:
flash("Missing admin_username parameter. (username)", "error")
return redirect(url_for("profile"))
return redirect(url_for("account"))
elif len(request.form["admin_username"]) > 256:
flash("The admin username is too long. It must be less than 256 characters. (username)", "error")
return redirect(url_for("profile"))
return redirect(url_for("account"))
username = request.form["admin_username"]
@ -571,20 +572,20 @@ def profile():
elif request.form["operation"] == "password":
if "admin_password" not in request.form:
flash("Missing admin_password parameter. (password)", "error")
return redirect(url_for("profile"))
return redirect(url_for("account"))
elif request.form.get("admin_password"):
if not request.form.get("admin_password_check"):
flash("Missing admin_password_check parameter. (password)", "error")
return redirect(url_for("profile"))
return redirect(url_for("account"))
elif request.form["admin_password"] != request.form["admin_password_check"]:
flash("The passwords does not match. (password)", "error")
return redirect(url_for("profile"))
return redirect(url_for("account"))
elif not USER_PASSWORD_RX.match(request.form["admin_password"]):
flash("The admin password is not strong enough. It must contain at least 8 characters, including at least 1 uppercase letter, 1 lowercase letter, 1 number and 1 special character (#@?!$%^&*-). (password)", "error")
return redirect(url_for("profile"))
return redirect(url_for("account"))
elif request.form.get("admin_password_check"):
flash("Missing admin_password parameter. (password)", "error")
return redirect(url_for("profile"))
return redirect(url_for("account"))
password = request.form["admin_password"]
@ -593,10 +594,10 @@ def profile():
elif request.form["operation"] == "totp":
if "totp_token" not in request.form:
flash("Missing totp_token parameter. (totp)", "error")
return redirect(url_for("profile"))
return redirect(url_for("account"))
elif not current_user.check_otp(request.form["totp_token"], secret=app.config["CURRENT_TOTP_TOKEN"]):
flash("The totp token is invalid. (totp)", "error")
return redirect(url_for("profile"))
return redirect(url_for("account"))
session["totp_validated"] = not current_user.is_two_factor_enabled
is_two_factor_enabled = session["totp_validated"]
@ -604,20 +605,20 @@ def profile():
app.config["CURRENT_TOTP_TOKEN"] = None
else:
flash("Invalid operation parameter.", "error")
return redirect(url_for("profile"))
return redirect(url_for("account"))
user = User(username, password, is_two_factor_enabled=is_two_factor_enabled, secret_token=secret_token, method=current_user.method)
ret = db.update_ui_user(username, user.password_hash, is_two_factor_enabled, secret_token, current_user.method if request.form["operation"] == "totp" else "ui")
if ret:
app.logger.error(f"Couldn't update the admin user in the database: {ret}")
flash(f"Couldn't update the admin user in the database: {ret}", "error")
return redirect(url_for("profile"))
return redirect(url_for("account"))
flash(
f"The {request.form['operation']} has been successfully updated." if request.form["operation"] != "totp" else f"The two-factor authentication was successfully {'disabled' if current_user.is_two_factor_enabled else 'enabled'}.",
)
return redirect(url_for("profile" if request.form["operation"] == "totp" else "login"))
return redirect(url_for("account" if request.form["operation"] == "totp" else "login"))
secret_token = ""
totp_qr_image = ""
@ -628,7 +629,7 @@ def profile():
totp_qr_image = get_b64encoded_qr_image(current_user.get_authentication_setup_uri())
app.config["CURRENT_TOTP_TOKEN"] = secret_token
return render_template("profile.html", username=current_user.get_id(), is_totp=current_user.is_two_factor_enabled, secret_token=secret_token, totp_qr_image=totp_qr_image, dark_mode=app.config["DARK_MODE"])
return render_template("account.html", username=current_user.get_id(), is_totp=current_user.is_two_factor_enabled, secret_token=secret_token, totp_qr_image=totp_qr_image, dark_mode=app.config["DARK_MODE"])
@app.route("/instances", methods=["GET", "POST"])
@ -674,6 +675,7 @@ def instances():
"instances.html",
title="Instances",
instances=instances,
username=current_user.get_id(),
dark_mode=app.config["DARK_MODE"],
)
@ -796,6 +798,7 @@ def services():
}
for service in services
],
username=current_user.get_id(),
dark_mode=app.config["DARK_MODE"],
)
@ -851,6 +854,7 @@ def global_config():
# Display global config
return render_template(
"global_config.html",
username=current_user.get_id(),
dark_mode=app.config["DARK_MODE"],
)
@ -945,6 +949,7 @@ def configs():
services=app.config["CONFIG"].get_config(methods=False).get("SERVER_NAME", "").split(" "),
)
],
username=current_user.get_id(),
dark_mode=app.config["DARK_MODE"],
)
@ -1193,6 +1198,7 @@ def plugins():
return template.render(
csrf_token=generate_csrf,
url_for=url_for,
username=current_user.get_id(),
dark_mode=app.config["DARK_MODE"],
**(plugin_args["args"] if plugin_args.get("plugin", None) == plugin_id else {}),
)
@ -1213,6 +1219,7 @@ def plugins():
plugins_internal=plugins_internal,
plugins_external=plugins_external,
plugins_errors=db.get_plugins_errors(),
username=current_user.get_id(),
dark_mode=app.config["DARK_MODE"],
)
@ -1350,6 +1357,7 @@ def cache():
services=app.config["CONFIG"].get_config(methods=False).get("SERVER_NAME", "").split(" "),
)
],
username=current_user.get_id(),
dark_mode=app.config["DARK_MODE"],
)
@ -1360,6 +1368,7 @@ def logs():
return render_template(
"logs.html",
instances=app.config["INSTANCES"].get_instances(),
username=current_user.get_id(),
dark_mode=app.config["DARK_MODE"],
)
@ -1587,6 +1596,7 @@ def jobs():
"jobs.html",
jobs=db.get_jobs(),
jobs_errors=db.get_plugins_errors(),
username=current_user.get_id(),
dark_mode=app.config["DARK_MODE"],
)

View file

@ -4,7 +4,6 @@
"requires": true,
"packages": {
"": {
"name": "ui",
"dependencies": {
"ace-builds": "^1.12.5",
"air-datepicker": "^3.3.1",

File diff suppressed because one or more lines are too long

View file

@ -1,6 +1,6 @@
import { Tabs, Popover } from "./utils/settings.js";
class SubmitProfile {
class SubmitAccount {
constructor() {
this.pwEl = document.querySelector("#admin_password");
this.pwCheckEl = document.querySelector("#admin_password_check");
@ -45,7 +45,7 @@ class SubmitProfile {
"focus:valid:!ring-red-500",
"active:!border-red-500",
"active:valid:!border-red-500",
"valid:!border-red-500",
"valid:!border-red-500"
);
this.pwAlertEl.classList.add("opacity-0");
this.pwAlertEl.setAttribute("aria-hidden", "true");
@ -59,7 +59,7 @@ class SubmitProfile {
"focus:valid:!ring-red-500",
"active:!border-red-500",
"active:valid:!border-red-500",
"valid:!border-red-500",
"valid:!border-red-500"
);
this.pwAlertEl.classList.remove("opacity-0");
this.pwAlertEl.setAttribute("aria-hidden", "false");
@ -77,14 +77,14 @@ class PwBtn {
const passwordContainer = e.target.closest("[data-input-group]");
const inpEl = passwordContainer.querySelector("input");
const invBtn = passwordContainer.querySelector(
'[data-setting-password="invisible"]',
'[data-setting-password="invisible"]'
);
const visBtn = passwordContainer.querySelector(
'[data-setting-password="visible"]',
'[data-setting-password="visible"]'
);
inpEl.setAttribute(
"type",
inpEl.getAttribute("type") === "password" ? "text" : "password",
inpEl.getAttribute("type") === "password" ? "text" : "password"
);
if (inpEl.getAttribute("type") === "password") {
@ -129,7 +129,7 @@ class SwitchTabForm {
}
const setPWBtn = new PwBtn();
const setSubmit = new SubmitProfile();
const setSubmit = new SubmitAccount();
const setTabs = new Tabs();
const setPopover = new Popover();
const setSwitchTabForm = new SwitchTabForm();

View file

@ -926,7 +926,7 @@ module.exports = {
"5/6": "83.333333%",
full: "100%",
// sidenav: "calc(100vh - 310px)",
sidenav: "calc(100vh - 360px)", // for pro btn
sidenav: "calc(100vh - 450px)", // for pro btn
screen: "100vh",
min: "min-content",
max: "max-content",

View file

@ -177,7 +177,7 @@ url_for(request.endpoint)[1:].split("/")[-1].strip() %}
<form
class="col-span-12 grid grid-cols-12 w-full justify-items-center"
id="username-form"
action="profile"
action="account"
method="POST"
autocomplete="off"
>
@ -282,7 +282,7 @@ url_for(request.endpoint)[1:].split("/")[-1].strip() %}
data-plugin-item="password"
class="hidden col-span-12 grid grid-cols-12 w-full justify-items-center mt-4"
id="password-form"
action="profile"
action="account"
method="POST"
autocomplete="off"
>
@ -471,13 +471,7 @@ url_for(request.endpoint)[1:].split("/")[-1].strip() %}
</div>
<div class="col-span-12 flex justify-center">
<button
type="submit"
id="pw-button"
name="pw-button"
value="profile"
class="edit-btn"
>
<button type="submit" id="pw-button" name="pw-button" class="edit-btn">
Edit
</button>
</div>
@ -487,7 +481,7 @@ url_for(request.endpoint)[1:].split("/")[-1].strip() %}
data-plugin-item="totp"
class="hidden grid grid-cols-12 w-full justify-items-center"
id="totp-form"
action="profile"
action="account"
method="POST"
autocomplete="off"
>

View file

@ -2,7 +2,7 @@
id="banner"
tabindex="-1"
role="list"
class="relative flex justify-center z-50 gap-8 px-4 w-full h-[4rem] z-100"
class="relative flex justify-center gap-8 px-4 w-full h-[4rem] z-100 overflow-hidden"
>
<!-- background-->
<div

View file

@ -31,7 +31,7 @@
<!-- info -->
<main
class="xl:pl-75 w-full px-2 sm:px-6 pb-0 pt-20 sm:pt-6 min-h-[85vh] h-full flex flex-col justify-between"
class="xl:pl-75 w-full px-2 sm:px-6 pb-0 pt-20 sm:pt-6 min-h-[85vh] flex flex-col justify-between"
>
<div
class="max-w-[1920px] grid gap-y-4 gap-3 sm:gap-4 lg:gap-6 grid-cols-12 w-full"

View file

@ -43,7 +43,7 @@
/>
{% elif current_endpoint == "jobs" %}
<script type="module" src="./js/jobs.js"></script>
{% elif current_endpoint == "profile" %}
<script type="module" src="./js/profile.js"></script>
{% elif current_endpoint == "account" %}
<script type="module" src="./js/account.js"></script>
{% endif %}
</head>

View file

@ -13,16 +13,14 @@
<h6 class="mb-0 text-lg font-bold text-white capitalize">
{{current_endpoint}}
</h6>
<ul
class="hidden xs:flex flex-col xs:flex-row flex-wrap pt-1 mr-12 bg-transparent rounded-lg sm:mr-16"
>
<ul class="flex flex-wrap pt-1 mr-12 bg-transparent rounded-lg sm:mr-16">
<li class="text-sm leading-normal">
<a class="text-white opacity-50 dark:opacity-75" href="javascript:;"
>BunkerWeb</a
>
</li>
<li
class="text-sm pl-0 xs:pl-2 capitalize leading-normal text-white before:float-left before:pr-2 before:text-white before:content-['/']"
class="hidden sm:inline text-sm pl-0 xs:pl-2 capitalize leading-normal text-white before:float-left before:pr-2 before:text-white before:content-['/']"
aria-current="page"
>
{{current_endpoint}}

View file

@ -30,9 +30,14 @@
id="sidebar-menu"
>
<!-- close btn-->
<button aria-controls="sidebar-menu" aria-expanded="false" aria-label="close menu sidebar" data-sidebar-menu-close>
<button
aria-controls="sidebar-menu"
aria-expanded="false"
aria-label="close menu sidebar"
data-sidebar-menu-close
>
<svg
class="xl:hidden fill-gray-600 dark:fill-gray-300 dark:opacity-80 absolute h-6 w-6 top-4 right-4"
class="xl:hidden fill-gray-600 dark:fill-gray-300 dark:opacity-80 absolute h-6 w-6 top-4 right-4"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 320 512"
>
@ -49,7 +54,7 @@
<a
aria-label="link to home"
class="flex justify-center px-8 py-6 m-0 text-sm whitespace-nowrap dark:text-white text-slate-700"
href="{% if current_endpoint == 'home' %}javascript:void(0){% else %}loading?next={{ url_for('home') }}{% endif %}"
href="{% if current_endpoint == 'home' %}#{% else %}loading?next={{ url_for('home') }}{% endif %}"
>
<img
src="images/logo-menu-2.png"
@ -64,6 +69,19 @@
</a>
</div>
<div class="w-full px-1">
<h1
class="mb-0.5 tracking-normal text-primary text-center text-lg break-words whitespace-normal dark:text-gray-300"
>
{{ username }}
</h1>
<a
class="block underline mb-2 text-gray-600 dark:text-gray-400 text-sm text-center hover:brightness-90"
href="{% if current_endpoint == 'account' %}#{% else %}loading?next={{ url_for('account') }}{% endif %}"
>manage account
</a>
</div>
<hr
class="h-px mt-0 bg-transparent bg-gradient-to-r from-transparent via-black/40 to-transparent dark:bg-gradient-to-r dark:from-transparent dark:via-white dark:to-transparent"
/>
@ -79,7 +97,7 @@
<li class="mt-0.5 w-full">
<a
class="{% if current_endpoint == 'home' %}font-semibold text-slate-700 dark:bg-primary/50 rounded-lg dark:hover:bg-primary/60 bg-primary/20 hover:bg-primary/30{% else %}dark:hover:bg-primary/20 hover:bg-primary/5 {% endif %} dark:text-white dark:opacity-80 py-1 ease-nav-brand my-0 mx-2 flex items-center whitespace-nowrap rounded-lg px-4 transition text-sm"
href="{% if current_endpoint == 'home' %}javascript:void(0){% else %}loading?next={{ url_for('home') }}{% endif %}"
href="{% if current_endpoint == 'home' %}#{% else %}loading?next={{ url_for('home') }}{% endif %}"
>
<div
class="mr-2 flex items-center justify-center rounded-lg bg-center stroke-0 text-center p-1 xl:p-1.5"
@ -110,7 +128,7 @@
<li class="mt-0.5 w-full">
<a
class="{% if current_endpoint == 'instances' %}font-semibold text-slate-700 dark:bg-primary/50 rounded-lg dark:hover:bg-primary/60 bg-primary/20 hover:bg-primary/30{% else %}dark:hover:bg-primary/20 hover:bg-primary/5 {% endif %} 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 current_endpoint == 'instances' %}javascript:void(0){% else %}loading?next={{ url_for('instances') }}{% endif %}"
href="{% if current_endpoint == 'instances' %}#{% else %}loading?next={{ url_for('instances') }}{% endif %}"
>
<div
class="mr-2 flex items-center justify-center rounded-lg bg-center stroke-0 text-center p-1 xl:p-1.5"
@ -140,7 +158,7 @@
<li class="mt-0.5 w-full">
<a
class="{% if current_endpoint == 'global_config' %}font-semibold text-slate-700 dark:bg-primary/50 rounded-lg dark:hover:bg-primary/60 bg-primary/20 hover:bg-primary/30{% else %}dark:hover:bg-primary/20 hover:bg-primary/5 {% endif %} 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 current_endpoint == 'global_config' %}javascript:void(0){% else %}loading?next={{ url_for('global_config') }}{% endif %}"
href="{% if current_endpoint == 'global_config' %}#{% else %}loading?next={{ url_for('global_config') }}{% endif %}"
>
<div
class="mr-2 flex items-center justify-center rounded-lg bg-center stroke-0 text-center p-1 xl:p-1.5"
@ -170,7 +188,7 @@
<li class="mt-0.5 w-full">
<a
class="{% if current_endpoint == 'services' %}font-semibold text-slate-700 dark:bg-primary/50 rounded-lg dark:hover:bg-primary/60 bg-primary/20 hover:bg-primary/30{% else %}dark:hover:bg-primary/20 hover:bg-primary/5 {% endif %} 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 current_endpoint == 'services' %}javascript:void(0){% else %}loading?next={{ url_for('services') }}{% endif %}"
href="{% if current_endpoint == 'services' %}#{% else %}loading?next={{ url_for('services') }}{% endif %}"
>
<div
class="mr-2 flex items-center justify-center rounded-lg bg-center stroke-0 text-center p-1 xl:p-1.5"
@ -200,7 +218,7 @@
<li class="mt-0.5 w-full">
<a
class="{% if current_endpoint == 'configs' %}font-semibold text-slate-700 dark:bg-primary/50 rounded-lg dark:hover:bg-primary/60 bg-primary/20 hover:bg-primary/30{% else %}dark:hover:bg-primary/20 hover:bg-primary/5 {% endif %} 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 current_endpoint == 'configs' %}javascript:void(0){% else %}loading?next={{ url_for('configs') }}{% endif %}"
href="{% if current_endpoint == 'configs' %}#{% else %}loading?next={{ url_for('configs') }}{% endif %}"
>
<div
class="mr-2 flex items-center justify-center rounded-lg bg-center stroke-0 text-center p-1 xl:p-1.5"
@ -235,7 +253,7 @@
<li class="mt-0.5 w-full">
<a
class="{% if current_endpoint == 'plugins' %}font-semibold text-slate-700 dark:bg-primary/50 rounded-lg dark:hover:bg-primary/60 bg-primary/20 hover:bg-primary/30{% else %}dark:hover:bg-primary/20 hover:bg-primary/5 {% endif %} 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 current_endpoint == 'plugins' %}javascript:void(0){% else %}loading?next={{ url_for('plugins') }}{% endif %}"
href="{% if current_endpoint == 'plugins' %}#{% else %}loading?next={{ url_for('plugins') }}{% endif %}"
>
<div
class="mr-2 flex items-center justify-center rounded-lg bg-center stroke-0 text-center p-1 xl:p-1.5"
@ -265,7 +283,7 @@
<li class="mt-0.5 w-full">
<a
class="{% if current_endpoint == 'cache' %}font-semibold text-slate-700 dark:bg-primary/50 rounded-lg dark:hover:bg-primary/60 bg-primary/20 hover:bg-primary/30{% else %}dark:hover:bg-primary/20 hover:bg-primary/5 {% endif %} 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 current_endpoint == 'cache' %}javascript:void(0){% else %}loading?next={{ url_for('cache') }}{% endif %}"
href="{% if current_endpoint == 'cache' %}#{% else %}loading?next={{ url_for('cache') }}{% endif %}"
>
<div
class="mr-2 flex items-center justify-center rounded-lg bg-center stroke-0 text-center p-1 xl:p-1.5"
@ -295,7 +313,7 @@
<li class="mt-0.5 w-full">
<a
class="{% if current_endpoint == 'logs' %}font-semibold text-slate-700 dark:bg-primary/50 rounded-lg dark:hover:bg-primary/60 bg-primary/20 hover:bg-primary/30{% else %}dark:hover:bg-primary/20 hover:bg-primary/5 {% endif %} 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 current_endpoint == 'logs' %}javascript:void(0){% else %}loading?next={{ url_for('logs') }}{% endif %}"
href="{% if current_endpoint == 'logs' %}#{% else %}loading?next={{ url_for('logs') }}{% endif %}"
>
<div
class="mr-2 flex items-center justify-center rounded-lg bg-center stroke-0 text-center p-1 xl:p-1.5"
@ -325,7 +343,7 @@
<li class="mt-0.5 w-full">
<a
class="{% if current_endpoint == 'jobs' %}font-semibold text-slate-700 dark:bg-primary/50 rounded-lg dark:hover:bg-primary/60 bg-primary/20 hover:bg-primary/30{% else %}dark:hover:bg-primary/20 hover:bg-primary/5 {% endif %} 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 current_endpoint == 'jobs' %}javascript:void(0){% else %}loading?next={{ url_for('jobs') }}{% endif %}"
href="{% if current_endpoint == 'jobs' %}#{% else %}loading?next={{ url_for('jobs') }}{% endif %}"
>
<div
class="mr-2 flex items-center justify-center rounded-lg bg-center stroke-0 text-center p-1 xl:p-1.5"
@ -350,38 +368,6 @@
>
</a>
</li>
<!-- end item -->
<li class="mt-0.5 w-full">
<a
class="{% if current_endpoint == 'profile' %}font-semibold text-slate-700 dark:bg-primary/50 rounded-lg dark:hover:bg-primary/60 bg-primary/20 hover:bg-primary/30{% else %}dark:hover:bg-primary/20 hover:bg-primary/5 {% endif %} 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 current_endpoint == 'profile' %}javascript:void(0){% else %}loading?next={{ url_for('profile') }}{% 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
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
class="stroke-teal-600 h-6 w-6 relative"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M17.982 18.725A7.488 7.488 0 0012 15.75a7.488 7.488 0 00-5.982 2.975m11.963 0a9 9 0 10-11.963 0m11.963 0A8.966 8.966 0 0112 21a8.966 8.966 0 01-5.982-2.275M15 9.75a3 3 0 11-6 0 3 3 0 016 0z"
/>
</svg>
</div>
<span
class="ml-1 duration-300 opacity-100 pointer-events-none ease"
>
Profile
</span>
</a>
</li>
<!-- end item -->
</ul>
<!-- end default anchor -->
@ -482,7 +468,7 @@
<!-- end dark/light mode -->
<!-- social-->
<ul class="flex justify-center align-middle w-full mb-4">
<ul class="mb-3 flex justify-center align-middle w-full">
<li class="mx-2 w-6">
<a
aria-label="link to twitter"

View file

@ -5,7 +5,7 @@
>
<div
data-plugins-modal-card
class="min-w-[500px ]overflow-y-auto mx-3 ml-2 mr-6 sm:mx-6 lg:mx-8 my-3 px-4 pt-4 pb-8 w-full max-w-[400px] flex flex-col break-words bg-white shadow-xl dark:bg-slate-850 dark:shadow-dark-xl rounded-2xl bg-clip-border"
class="overflow-y-auto mx-0 sm:mx-6 lg:mx-8 my-3 px-4 pt-4 pb-8 w-full sm:min-w-[500px] h-[90vh] flex flex-col break-words bg-white shadow-xl dark:bg-slate-850 dark:shadow-dark-xl rounded-2xl bg-clip-border"
>
<div class="w-full flex justify-between mb-2">
<p

View file

@ -15,7 +15,7 @@
<!-- end actions -->
<!-- services container-->
<div
class="gap-8 p-4 grid grid-cols-12 col-span-12 relative min-w-0 break-words rounded-2xl bg-clip-border"
class="p-0 my-4 sm:mx-4 md:px-8 grid grid-cols-12 col-span-12 relative min-w-0 break-words rounded-2xl bg-clip-border"
>
{% if services|length == 0 %}
<div class="col-span-12 sm:col-span-4 sm:col-start-5">

View file

@ -6,7 +6,7 @@
>
<div
data-services-modal-card
class="overflow-y-auto mx-3 ml-2 mr-6 sm:mx-6 lg:mx-8 my-3 px-4 pt-4 pb-8 w-full sm:min-w-[500px] h-[90vh] flex flex-col break-words bg-white shadow-xl dark:bg-slate-850 dark:shadow-dark-xl rounded-2xl bg-clip-border"
class="overflow-y-auto mx-0 sm:mx-6 lg:mx-8 my-3 px-4 pt-4 pb-8 w-full sm:min-w-[500px] h-[90vh] flex flex-col break-words bg-white shadow-xl dark:bg-slate-850 dark:shadow-dark-xl rounded-2xl bg-clip-border"
>
<div class="w-full flex justify-between mb-2">
<p
@ -33,7 +33,7 @@
</div>
<div
data-services-tabs-header
class="flex justify-start items-center gap-x-4 gap-y-2 my-3"
class="flex flex-col sm:flex-row justify-start items-start sm:items-center gap-x-4 gap-y-2 my-3"
>
<h5
class="transition duration-300 ease-in-out dark:opacity-90 ml-2 font-bold text-md uppercase dark:text-white mb-0"
@ -41,9 +41,7 @@
CONFIGS
</h5>
<!-- search inpt-->
<div
class="flex relative col-span-12 sm:col-span-6 lg:col-span-4 3xl:col-span-3"
>
<div class="flex relative">
<label class="sr-only" for="settings-filter">search</label>
<input
type="text"