mirror of
https://gitlab.com/crafty-controller/crafty-4.git
synced 2024-08-30 18:23:09 +00:00
Merge branch 'dev' into 'master'
v4.0.21 See merge request crafty-controller/crafty-4!568
This commit is contained in:
commit
d03966a54f
33
CHANGELOG.md
33
CHANGELOG.md
@ -1,5 +1,34 @@
|
||||
# Changelog
|
||||
## --- [4.0.20] - 2022/01/29
|
||||
## --- [4.0.21] - 2023/03/04
|
||||
### New features
|
||||
- Add better feedback for uploads with a progress bar ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/546))
|
||||
- Add ignored exit codes for crash detection ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/553))
|
||||
- Allow users to change the directory where Crafty Stores Servers ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/539)) <br>
|
||||
*(Only for non-docker, docker users should change host volume mount)*
|
||||
- Add host storage display option to the dashboard ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/551))
|
||||
### Bug fixes
|
||||
- Fix exception related to page data on server start ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/544))
|
||||
- Fix logical issue with uploading dynamic files ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/555))
|
||||
- Fix backups failing by correctly using tz objects ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/556))
|
||||
- Bump Cryptography/pyOpenSSL for CVE-2023-23931 ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/554))
|
||||
- Fix debug logging to only display with the -v (verbose) flag ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/560))
|
||||
- Optimize world size calculation ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/550))
|
||||
- Only copy bedrock_server executable on update ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/562))
|
||||
- Fix bug where unloaded servers could not be deleted ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/566))
|
||||
- Fix bug where "servers" was not appended ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/567))
|
||||
### Tweaks
|
||||
- Cleanup authentication helpers ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/545))
|
||||
- Optimize file upload progress WS ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/546))
|
||||
- Truncate sidebar servers to a max of 10 ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/552))
|
||||
- Upgrade to FA 6. Add Translations ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/549))([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/558))
|
||||
- Forge installer and Java Detection improvements ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/559))
|
||||
- Crafty log clean up -config option ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/563))
|
||||
### Lang
|
||||
- Add additional translations to backups page strings ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/543))
|
||||
- Add additional missing translations ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/549))
|
||||
<br><br>
|
||||
|
||||
## --- [4.0.20] - 2023/01/29
|
||||
### New features
|
||||
- Add option to run command before backup. ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/536))
|
||||
- Make Config.json editable from panel. ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/532))
|
||||
@ -19,7 +48,7 @@
|
||||
- Add Default redirection to Dashboard if the user is connected. ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/540))
|
||||
<br><br>
|
||||
|
||||
## --- [4.0.19] - 2022/01/07
|
||||
## --- [4.0.19] - 2023/01/07
|
||||
### Bug fixes
|
||||
- Fix port tooltip not showing on dash while server online. ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/503))
|
||||
- Fix '+' char in path causing any file operation to fail. ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/502))
|
||||
|
@ -1,5 +1,5 @@
|
||||
[![Crafty Logo](app/frontend/static/assets/images/logo_long.svg)](https://craftycontrol.com)
|
||||
# Crafty Controller 4.0.20
|
||||
# Crafty Controller 4.0.21
|
||||
> Python based Control Panel for your Minecraft Server
|
||||
|
||||
## What is Crafty Controller?
|
||||
@ -18,6 +18,8 @@ Discord Server - https://discord.gg/9VJPhCE
|
||||
|
||||
Git Repository - https://gitlab.com/crafty-controller/crafty-4
|
||||
|
||||
Docker Hub - [arcadiatechnology/crafty-4](https://hub.docker.com/r/arcadiatechnology/crafty-4)
|
||||
|
||||
<br>
|
||||
|
||||
## Basic Docker Usage 🐳
|
||||
|
@ -195,3 +195,14 @@ class ManagementController:
|
||||
|
||||
def del_excluded_backup_dir(self, server_id: int, dir_to_del: str):
|
||||
self.management_helper.del_excluded_backup_dir(server_id, dir_to_del)
|
||||
|
||||
# **********************************************************************************
|
||||
# Crafty Methods
|
||||
# **********************************************************************************
|
||||
@staticmethod
|
||||
def get_master_server_dir():
|
||||
return HelpersManagement.get_master_server_dir()
|
||||
|
||||
@staticmethod
|
||||
def set_master_server_dir(server_dir):
|
||||
HelpersManagement.set_master_server_dir(server_dir)
|
||||
|
@ -102,6 +102,7 @@ class ServersController(metaclass=Singleton):
|
||||
server_obj.server_id
|
||||
)
|
||||
server_instance.update_server_instance()
|
||||
|
||||
return ret
|
||||
|
||||
def get_history_stats(self, server_id, days):
|
||||
@ -163,10 +164,9 @@ class ServersController(metaclass=Singleton):
|
||||
return server["server_obj"]
|
||||
|
||||
logger.warning(f"Unable to find server object for server id {server_id}")
|
||||
raise Exception(f"Unable to find server object for server id {server_id}")
|
||||
raise ValueError(f"Unable to find server object for server id {server_id}")
|
||||
|
||||
def init_all_servers(self):
|
||||
|
||||
servers = self.get_all_defined_servers()
|
||||
self.failed_servers = []
|
||||
|
||||
@ -227,7 +227,6 @@ class ServersController(metaclass=Singleton):
|
||||
)
|
||||
|
||||
def check_server_loaded(self, server_id_to_check: int):
|
||||
|
||||
logger.info(f"Checking to see if we already registered {server_id_to_check}")
|
||||
|
||||
for server in self.servers_list:
|
||||
|
@ -19,7 +19,6 @@ class Server:
|
||||
self.description = data.get("description")
|
||||
# print(self.description)
|
||||
if isinstance(self.description, dict):
|
||||
|
||||
# cat server
|
||||
if "translate" in self.description:
|
||||
self.description = self.description["translate"]
|
||||
@ -124,7 +123,7 @@ def ping(ip, port):
|
||||
try:
|
||||
k = sock.recv(1)
|
||||
if not k:
|
||||
raise Exception()
|
||||
raise ValueError()
|
||||
except:
|
||||
return 0
|
||||
k = k[0]
|
||||
|
@ -104,7 +104,6 @@ class ServerJars:
|
||||
logger.error(f"Unable to update serverjars.com cache file: {e}")
|
||||
|
||||
def refresh_cache(self):
|
||||
|
||||
cache_file = self.helper.serverjar_cache
|
||||
cache_old = self.helper.is_file_older_than_x_days(cache_file)
|
||||
|
||||
|
@ -210,8 +210,7 @@ class Stats:
|
||||
return disk_data
|
||||
|
||||
@staticmethod
|
||||
def get_world_size(server_path):
|
||||
|
||||
def get_server_dir_size(server_path):
|
||||
total_size = 0
|
||||
|
||||
total_size = Helpers.get_dir_size(server_path)
|
||||
@ -221,7 +220,6 @@ class Stats:
|
||||
return level_total_size
|
||||
|
||||
def get_server_players(self, server_id):
|
||||
|
||||
server = HelperServers.get_server_data_by_id(server_id)
|
||||
|
||||
logger.info(f"Getting players for server {server}")
|
||||
@ -295,7 +293,6 @@ class Stats:
|
||||
|
||||
@staticmethod
|
||||
def parse_server_raknet_ping(ping_obj: object):
|
||||
|
||||
try:
|
||||
server_icon = base64.encodebytes(ping_obj["icon"])
|
||||
except Exception as e:
|
||||
|
@ -15,6 +15,7 @@ from app.classes.shared.permission_helper import PermissionHelper
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
# **********************************************************************************
|
||||
# User_Crafty Class
|
||||
# **********************************************************************************
|
||||
|
@ -20,6 +20,7 @@ from app.classes.shared.main_models import DatabaseShortcuts
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
# **********************************************************************************
|
||||
# Audit_Log Class
|
||||
# **********************************************************************************
|
||||
@ -46,6 +47,7 @@ class CraftySettings(BaseModel):
|
||||
cookie_secret = CharField(default="")
|
||||
login_photo = CharField(default="login_1.jpg")
|
||||
login_opacity = IntegerField(default=100)
|
||||
master_server_dir = CharField(default="")
|
||||
|
||||
class Meta:
|
||||
table_name = "crafty_settings"
|
||||
@ -271,6 +273,19 @@ class HelpersManagement:
|
||||
CraftySettings.id == 1
|
||||
).execute()
|
||||
|
||||
@staticmethod
|
||||
def get_master_server_dir():
|
||||
settings = CraftySettings.select(CraftySettings.master_server_dir).where(
|
||||
CraftySettings.id == 1
|
||||
)
|
||||
return settings[0].master_server_dir
|
||||
|
||||
@staticmethod
|
||||
def set_master_server_dir(server_dir):
|
||||
CraftySettings.update({CraftySettings.master_server_dir: server_dir}).where(
|
||||
CraftySettings.id == 1
|
||||
).execute()
|
||||
|
||||
# **********************************************************************************
|
||||
# Schedules Methods
|
||||
# **********************************************************************************
|
||||
|
@ -15,6 +15,7 @@ from app.classes.shared.helpers import Helpers
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
# **********************************************************************************
|
||||
# Roles Class
|
||||
# **********************************************************************************
|
||||
|
@ -16,6 +16,7 @@ from app.classes.shared.permission_helper import PermissionHelper
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
# **********************************************************************************
|
||||
# Role Servers Class
|
||||
# **********************************************************************************
|
||||
|
@ -29,6 +29,7 @@ logger = logging.getLogger(__name__)
|
||||
peewee_logger = logging.getLogger("peewee")
|
||||
peewee_logger.setLevel(logging.INFO)
|
||||
|
||||
|
||||
# **********************************************************************************
|
||||
# Servers Stats Class
|
||||
# **********************************************************************************
|
||||
|
@ -15,6 +15,7 @@ from app.classes.models.base_model import BaseModel
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
# **********************************************************************************
|
||||
# Servers Model
|
||||
# **********************************************************************************
|
||||
@ -40,6 +41,7 @@ class Servers(BaseModel):
|
||||
show_status = BooleanField(default=1)
|
||||
created_by = IntegerField(default=-100)
|
||||
shutdown_timeout = IntegerField(default=60)
|
||||
ignored_exits = CharField(default="0")
|
||||
|
||||
class Meta:
|
||||
table_name = "servers"
|
||||
|
@ -21,6 +21,7 @@ from app.classes.models.roles import Roles, HelperRoles
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
# **********************************************************************************
|
||||
# Users Class
|
||||
# **********************************************************************************
|
||||
@ -58,6 +59,7 @@ PUBLIC_USER_ATTRS: t.Final = [
|
||||
"lang", # maybe remove?
|
||||
]
|
||||
|
||||
|
||||
# **********************************************************************************
|
||||
# API Keys Class
|
||||
# **********************************************************************************
|
||||
|
@ -13,7 +13,6 @@ logger = logging.getLogger(__name__)
|
||||
class Authentication:
|
||||
def __init__(self, helper):
|
||||
self.helper = helper
|
||||
self.secret = "my secret"
|
||||
try:
|
||||
self.secret = ManagementController.get_crafty_api_key()
|
||||
if self.secret == "":
|
||||
@ -77,7 +76,7 @@ class Authentication:
|
||||
|
||||
output = self.check(token)
|
||||
if output is None:
|
||||
raise Exception("Invalid token")
|
||||
raise ValueError("Invalid token")
|
||||
return output
|
||||
|
||||
def check_bool(self, token) -> bool:
|
||||
|
@ -58,7 +58,6 @@ class MainPrompt(cmd.Cmd):
|
||||
Console.info("Unknown migration command")
|
||||
|
||||
def do_set_passwd(self, line):
|
||||
|
||||
try:
|
||||
username = str(line).lower()
|
||||
# If no user is found it returns None
|
||||
|
@ -19,6 +19,8 @@ except ModuleNotFoundError as ex:
|
||||
|
||||
|
||||
class Console:
|
||||
level = ""
|
||||
|
||||
def __init__(self):
|
||||
if "colorama" in sys.modules:
|
||||
init()
|
||||
@ -61,8 +63,9 @@ class Console:
|
||||
|
||||
@staticmethod
|
||||
def debug(message):
|
||||
date_time = Console.get_fmt_date_time()
|
||||
Console.magenta(f"[+] Crafty: {date_time} - DEBUG:\t{message}")
|
||||
if Console.level == "debug":
|
||||
date_time = Console.get_fmt_date_time()
|
||||
Console.magenta(f"[+] Crafty: {date_time} - DEBUG:\t{message}")
|
||||
|
||||
@staticmethod
|
||||
def info(message):
|
||||
|
@ -283,27 +283,31 @@ class FileHelpers:
|
||||
return True
|
||||
|
||||
@staticmethod
|
||||
def unzip_file(zip_path):
|
||||
new_dir_list = zip_path.split("/")
|
||||
new_dir = ""
|
||||
for i in range(len(new_dir_list) - 1):
|
||||
if i == 0:
|
||||
new_dir += new_dir_list[i]
|
||||
else:
|
||||
new_dir += "/" + new_dir_list[i]
|
||||
|
||||
def unzip_file(zip_path, server_update=False):
|
||||
ignored_names = ["server.properties", "permissions.json", "allowlist.json"]
|
||||
# Get directory without zipfile name
|
||||
new_dir = pathlib.Path(zip_path).parents[0]
|
||||
# make sure we're able to access the zip file
|
||||
if Helpers.check_file_perms(zip_path) and os.path.isfile(zip_path):
|
||||
# make sure the directory we're unzipping this to exists
|
||||
Helpers.ensure_dir_exists(new_dir)
|
||||
# we'll make a temporary directory to unzip this to.
|
||||
temp_dir = tempfile.mkdtemp()
|
||||
try:
|
||||
with zipfile.ZipFile(zip_path, "r") as zip_ref:
|
||||
# we'll extract this to the temp dir using zipfile module
|
||||
zip_ref.extractall(temp_dir)
|
||||
full_root_path = temp_dir
|
||||
for item in os.listdir(full_root_path):
|
||||
if os.path.isdir(os.path.join(full_root_path, item)):
|
||||
# we'll iterate through the top level directory moving everything
|
||||
# out of the temp directory and into it's final home.
|
||||
for item in os.listdir(temp_dir):
|
||||
# if the file is one of our ignored names we'll skip it
|
||||
if item in ignored_names and server_update:
|
||||
continue
|
||||
# we handle files and dirs differently or we'll crash out.
|
||||
if os.path.isdir(os.path.join(temp_dir, item)):
|
||||
try:
|
||||
FileHelpers.move_dir_exist(
|
||||
os.path.join(full_root_path, item),
|
||||
os.path.join(temp_dir, item),
|
||||
os.path.join(new_dir, item),
|
||||
)
|
||||
except Exception as ex:
|
||||
@ -311,7 +315,7 @@ class FileHelpers:
|
||||
else:
|
||||
try:
|
||||
FileHelpers.move_file(
|
||||
os.path.join(full_root_path, item),
|
||||
os.path.join(temp_dir, item),
|
||||
os.path.join(new_dir, item),
|
||||
)
|
||||
except Exception as ex:
|
||||
|
@ -15,6 +15,7 @@ import html
|
||||
import zipfile
|
||||
import pathlib
|
||||
import ctypes
|
||||
import shutil
|
||||
import subprocess
|
||||
import itertools
|
||||
from datetime import datetime
|
||||
@ -62,6 +63,7 @@ class Helpers:
|
||||
self.servers_dir = os.path.join(self.root_dir, "servers")
|
||||
self.backup_path = os.path.join(self.root_dir, "backups")
|
||||
self.migration_dir = os.path.join(self.root_dir, "app", "migrations")
|
||||
self.dir_migration = False
|
||||
|
||||
self.session_file = os.path.join(self.root_dir, "app", "config", "session.lock")
|
||||
self.settings_file = os.path.join(self.root_dir, "app", "config", "config.json")
|
||||
@ -94,7 +96,7 @@ class Helpers:
|
||||
try:
|
||||
# Get tags from Gitlab, select the latest and parse the semver
|
||||
response = get(
|
||||
"https://gitlab.com/api/v4/projects/20430749/repository/tags"
|
||||
"https://gitlab.com/api/v4/projects/20430749/repository/tags", timeout=1
|
||||
)
|
||||
if response.status_code == 200:
|
||||
remote_version = pkg_version.parse(json.loads(response.text)[0]["name"])
|
||||
@ -131,7 +133,7 @@ class Helpers:
|
||||
try:
|
||||
# Get minecraft server download page
|
||||
# (hopefully the don't change the structure)
|
||||
download_page = get(url, headers=headers)
|
||||
download_page = get(url, headers=headers, timeout=1)
|
||||
|
||||
# Search for our string targets
|
||||
win_download_url = re.search(target_win, download_page.text).group(0)
|
||||
@ -145,6 +147,22 @@ class Helpers:
|
||||
logger.error(f"Unable to resolve remote bedrock download url! \n{e}")
|
||||
return False
|
||||
|
||||
def detect_java(self):
|
||||
if len(self.find_java_installs()) > 0:
|
||||
return True
|
||||
|
||||
# We'll use this as a fallback for systems
|
||||
# That do not properly setup reg keys or
|
||||
# Update alternatives
|
||||
if self.is_os_windows():
|
||||
if shutil.which("java.exe"):
|
||||
return True
|
||||
else:
|
||||
if shutil.which("java"):
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def find_java_installs():
|
||||
# If we're windows return oracle java versions,
|
||||
@ -281,7 +299,7 @@ class Helpers:
|
||||
@staticmethod
|
||||
def check_port(server_port):
|
||||
try:
|
||||
ip = get("https://api.ipify.org").content.decode("utf8")
|
||||
ip = get("https://api.ipify.org", timeout=1).content.decode("utf8")
|
||||
except:
|
||||
ip = "google.com"
|
||||
a_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
@ -395,6 +413,10 @@ class Helpers:
|
||||
|
||||
@staticmethod
|
||||
def get_master_config():
|
||||
# Let's get the mounts and only show the first one by default
|
||||
mounts = Helpers.get_all_mounts()
|
||||
if len(mounts) != 0:
|
||||
mounts = mounts[0]
|
||||
# Make changes for users' local config.json files here. As of 4.0.20
|
||||
# Config.json was removed from the repo to make it easier for users
|
||||
# To make non-breaking changes to the file.
|
||||
@ -405,7 +427,7 @@ class Helpers:
|
||||
"cookie_expire": 30,
|
||||
"show_errors": True,
|
||||
"history_max_age": 7,
|
||||
"stats_update_frequency": 30,
|
||||
"stats_update_frequency_seconds": 30,
|
||||
"delete_default_json": False,
|
||||
"show_contribute_link": True,
|
||||
"virtual_terminal_lines": 70,
|
||||
@ -417,6 +439,9 @@ class Helpers:
|
||||
"allow_nsfw_profile_pictures": False,
|
||||
"enable_user_self_delete": False,
|
||||
"reset_secrets_on_next_boot": False,
|
||||
"monitored_mounts": mounts,
|
||||
"dir_size_poll_freq_minutes": 5,
|
||||
"crafty_logs_delete_after_days": 0,
|
||||
}
|
||||
|
||||
def get_all_settings(self):
|
||||
@ -436,11 +461,27 @@ class Helpers:
|
||||
return data
|
||||
|
||||
@staticmethod
|
||||
def is_subdir(server_path, root_dir):
|
||||
def get_all_mounts():
|
||||
mounts = []
|
||||
for item in psutil.disk_partitions(all=False):
|
||||
mounts.append(item.mountpoint)
|
||||
|
||||
return mounts
|
||||
|
||||
def is_subdir(self, server_path, root_dir):
|
||||
server_path = os.path.realpath(server_path)
|
||||
root_dir = os.path.realpath(root_dir)
|
||||
|
||||
relative = os.path.relpath(server_path, root_dir)
|
||||
if self.is_os_windows():
|
||||
try:
|
||||
relative = os.path.relpath(server_path, root_dir)
|
||||
except:
|
||||
# Windows will crash out if two paths are on different
|
||||
# Drives We can happily return false if this is the case.
|
||||
# Since two different drives will not be relative to eachother.
|
||||
return False
|
||||
else:
|
||||
relative = os.path.relpath(server_path, root_dir)
|
||||
|
||||
if relative.startswith(os.pardir):
|
||||
return False
|
||||
@ -596,7 +637,6 @@ class Helpers:
|
||||
|
||||
# open our file
|
||||
with open(file_name, "r", encoding="utf-8") as f:
|
||||
|
||||
# seek
|
||||
f.seek(0, 2)
|
||||
|
||||
@ -752,7 +792,7 @@ class Helpers:
|
||||
use_ssl=True,
|
||||
) # + "?d=404"
|
||||
try:
|
||||
if requests.head(url).status_code != 404:
|
||||
if requests.head(url, timeout=1).status_code != 404:
|
||||
profile_url = url
|
||||
except Exception as e:
|
||||
logger.debug(f"Could not pull resource from Gravatar with error {e}")
|
||||
@ -761,7 +801,6 @@ class Helpers:
|
||||
|
||||
@staticmethod
|
||||
def get_file_contents(path: str, lines=100):
|
||||
|
||||
contents = ""
|
||||
|
||||
if os.path.exists(path) and os.path.isfile(path):
|
||||
@ -782,12 +821,10 @@ class Helpers:
|
||||
return False
|
||||
|
||||
def create_session_file(self, ignore=False):
|
||||
|
||||
if ignore and os.path.exists(self.session_file):
|
||||
os.remove(self.session_file)
|
||||
|
||||
if os.path.exists(self.session_file):
|
||||
|
||||
file_data = self.get_file_contents(self.session_file)
|
||||
try:
|
||||
data = json.loads(file_data)
|
||||
@ -887,15 +924,16 @@ class Helpers:
|
||||
try:
|
||||
os.makedirs(path)
|
||||
logger.debug(f"Created Directory : {path}")
|
||||
return True
|
||||
|
||||
# directory already exists - non-blocking error
|
||||
except FileExistsError:
|
||||
pass
|
||||
return True
|
||||
except PermissionError as e:
|
||||
logger.critical(f"Check generated exception due to permssion error: {e}")
|
||||
return False
|
||||
|
||||
def create_self_signed_cert(self, cert_dir=None):
|
||||
|
||||
if cert_dir is None:
|
||||
cert_dir = os.path.join(self.config_dir, "web", "certs")
|
||||
|
||||
@ -977,6 +1015,15 @@ class Helpers:
|
||||
def is_os_windows():
|
||||
return os.name == "nt"
|
||||
|
||||
@staticmethod
|
||||
def is_env_docker():
|
||||
path = "/proc/self/cgroup"
|
||||
return (
|
||||
os.path.exists("/.dockerenv")
|
||||
or os.path.isfile(path)
|
||||
and any("docker" in line for line in open(path, encoding="utf-8"))
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def wtol_path(w_path):
|
||||
l_path = w_path.replace("\\", "/")
|
||||
@ -1013,7 +1060,7 @@ class Helpers:
|
||||
for item in file_list:
|
||||
if os.path.isdir(os.path.join(folder, item)):
|
||||
dir_list.append(item)
|
||||
elif str(item) != "crafty.sqlite":
|
||||
elif str(item) != self.ignored_names:
|
||||
unsorted_files.append(item)
|
||||
file_list = sorted(dir_list, key=str.casefold) + sorted(
|
||||
unsorted_files, key=str.casefold
|
||||
@ -1026,9 +1073,9 @@ class Helpers:
|
||||
if filename not in self.ignored_names:
|
||||
output += f"""<li id="{dpath}li" class="tree-item"
|
||||
data-path="{dpath}">
|
||||
\n<div id="{dpath}" data-path="{dpath}" data-name="{filename}"
|
||||
\n<div id="{dpath}" data-path="{dpath}" data-name="{filename}"
|
||||
class="tree-caret tree-ctx-item tree-folder">
|
||||
<span id="{dpath}span" class="files-tree-title" data-path="{dpath}"
|
||||
<span id="{dpath}span" class="files-tree-title" data-path="{dpath}"
|
||||
data-name="{filename}" onclick="getDirView(event)">
|
||||
<i style="color: var(--info);" class="far fa-folder"></i>
|
||||
<i style="color: var(--info);" class="far fa-folder-open"></i>
|
||||
@ -1047,14 +1094,13 @@ class Helpers:
|
||||
return output
|
||||
|
||||
def generate_dir(self, folder, output=""):
|
||||
|
||||
dir_list = []
|
||||
unsorted_files = []
|
||||
file_list = os.listdir(folder)
|
||||
for item in file_list:
|
||||
if os.path.isdir(os.path.join(folder, item)):
|
||||
dir_list.append(item)
|
||||
elif str(item) != "crafty.sqlite":
|
||||
elif str(item) != self.ignored_names:
|
||||
unsorted_files.append(item)
|
||||
file_list = sorted(dir_list, key=str.casefold) + sorted(
|
||||
unsorted_files, key=str.casefold
|
||||
@ -1068,9 +1114,9 @@ class Helpers:
|
||||
if filename not in self.ignored_names:
|
||||
output += f"""<li id="{dpath}li" class="tree-item"
|
||||
data-path="{dpath}">
|
||||
\n<div id="{dpath}" data-path="{dpath}" data-name="{filename}"
|
||||
\n<div id="{dpath}" data-path="{dpath}" data-name="{filename}"
|
||||
class="tree-caret tree-ctx-item tree-folder">
|
||||
<span id="{dpath}span" class="files-tree-title" data-path="{dpath}"
|
||||
<span id="{dpath}span" class="files-tree-title" data-path="{dpath}"
|
||||
data-name="{filename}" onclick="getDirView(event)">
|
||||
<i style="color: var(--info);" class="far fa-folder"></i>
|
||||
<i style="color: var(--info);" class="far fa-folder-open"></i>
|
||||
|
@ -226,7 +226,6 @@ class ImportHelpers:
|
||||
download_thread.start()
|
||||
|
||||
def download_threaded_bedrock_server(self, path, new_id):
|
||||
|
||||
# downloads zip from remote url
|
||||
try:
|
||||
bedrock_url = Helpers.get_latest_bedrock_url()
|
||||
|
@ -10,7 +10,6 @@ class Install:
|
||||
)
|
||||
|
||||
def do_install(self):
|
||||
|
||||
# are we in a venv?
|
||||
if not self.is_venv():
|
||||
print("Crafty Requires a venv to install")
|
||||
|
@ -6,6 +6,7 @@ import platform
|
||||
import shutil
|
||||
import time
|
||||
import logging
|
||||
import threading
|
||||
from peewee import DoesNotExist
|
||||
|
||||
# TZLocal is set as a hidden import on win pipeline
|
||||
@ -347,7 +348,7 @@ class Controller:
|
||||
elif root_create_data["create_type"] == "import_zip":
|
||||
# TODO: Copy files from the zip file to the new server directory
|
||||
server_file = create_data["jarfile"]
|
||||
raise Exception("Not yet implemented")
|
||||
raise NotImplementedError("Not yet implemented")
|
||||
_create_server_properties_if_needed(
|
||||
create_data["server_properties_port"],
|
||||
)
|
||||
@ -379,7 +380,7 @@ class Controller:
|
||||
logger.error(f"Server import failed with error: {ex}")
|
||||
elif root_create_data["create_type"] == "import_zip":
|
||||
# TODO: Copy files from the zip file to the new server directory
|
||||
raise Exception("Not yet implemented")
|
||||
raise NotImplementedError("Not yet implemented")
|
||||
|
||||
_create_server_properties_if_needed(0, True)
|
||||
|
||||
@ -401,7 +402,7 @@ class Controller:
|
||||
logger.error(f"Server import failed with error: {ex}")
|
||||
elif root_create_data["create_type"] == "import_zip":
|
||||
# TODO: Copy files from the zip file to the new server directory
|
||||
raise Exception("Not yet implemented")
|
||||
raise NotImplementedError("Not yet implemented")
|
||||
|
||||
_create_server_properties_if_needed(0, True)
|
||||
|
||||
@ -941,7 +942,6 @@ class Controller:
|
||||
def remove_server(self, server_id, files):
|
||||
counter = 0
|
||||
for server in self.servers.servers_list:
|
||||
|
||||
# if this is the droid... im mean server we are looking for...
|
||||
if str(server["server_id"]) == str(server_id):
|
||||
server_data = self.servers.get_server_data(server_id)
|
||||
@ -1003,3 +1003,125 @@ class Controller:
|
||||
@staticmethod
|
||||
def clear_support_status():
|
||||
HelperUsers.clear_support_status()
|
||||
|
||||
def set_master_server_dir(self, server_dir):
|
||||
# This method should only be used on a first run basis if the server dir is ""
|
||||
self.helper.servers_dir = server_dir
|
||||
HelpersManagement.set_master_server_dir(server_dir)
|
||||
|
||||
def update_master_server_dir(self, server_dir, user_id):
|
||||
self.helper.dir_migration = True
|
||||
move_thread = threading.Thread(
|
||||
name="dir_move",
|
||||
target=self.t_update_master_server_dir,
|
||||
daemon=True,
|
||||
args=(
|
||||
server_dir,
|
||||
user_id,
|
||||
),
|
||||
)
|
||||
move_thread.start()
|
||||
|
||||
def t_update_master_server_dir(self, new_server_path, user_id):
|
||||
new_server_path = self.helper.wtol_path(new_server_path)
|
||||
new_server_path = os.path.join(new_server_path, "servers")
|
||||
self.helper.websocket_helper.broadcast_page(
|
||||
"/panel/panel_config", "move_status", "Checking dir"
|
||||
)
|
||||
current_master = self.helper.wtol_path(
|
||||
HelpersManagement.get_master_server_dir()
|
||||
)
|
||||
if current_master == new_server_path:
|
||||
logger.info(
|
||||
"Admin tried to change server dir to current server dir. Canceling..."
|
||||
)
|
||||
self.helper.websocket_helper.broadcast_page(
|
||||
"/panel/panel_config",
|
||||
"move_status",
|
||||
"done",
|
||||
)
|
||||
return
|
||||
if self.helper.is_subdir(new_server_path, current_master):
|
||||
logger.info(
|
||||
"Admin tried to change server dir to be inside a sub directory of the"
|
||||
" current server dir. This will result in a copy loop."
|
||||
)
|
||||
self.helper.websocket_helper.broadcast_page(
|
||||
"/panel/panel_config",
|
||||
"move_status",
|
||||
"done",
|
||||
)
|
||||
return
|
||||
|
||||
self.helper.websocket_helper.broadcast_page(
|
||||
"/panel/panel_config", "move_status", "Checking permissions"
|
||||
)
|
||||
if not self.helper.ensure_dir_exists(new_server_path):
|
||||
self.helper.websocket_helper.broadcast_user(
|
||||
user_id,
|
||||
"send_start_error",
|
||||
{
|
||||
"error": "Crafty failed to move server dir. "
|
||||
"It seems Crafty lacks permission to write to "
|
||||
"the new directory."
|
||||
},
|
||||
)
|
||||
return
|
||||
# set the cached serve dir
|
||||
self.helper.servers_dir = new_server_path
|
||||
# set DB server dir
|
||||
HelpersManagement.set_master_server_dir(new_server_path)
|
||||
servers = self.servers.get_all_defined_servers()
|
||||
# move the servers
|
||||
for server in servers:
|
||||
server_path = server.get("path")
|
||||
new_local_server_path = os.path.join(
|
||||
new_server_path, server.get("server_uuid")
|
||||
)
|
||||
if os.path.isdir(server_path):
|
||||
self.helper.websocket_helper.broadcast_page(
|
||||
"/panel/panel_config",
|
||||
"move_status",
|
||||
f"Moving {server.get('server_name')}",
|
||||
)
|
||||
try:
|
||||
self.file_helper.move_dir(
|
||||
server_path,
|
||||
new_local_server_path,
|
||||
)
|
||||
except FileExistsError as e:
|
||||
logger.error(f"Failed to move server with error: {e}")
|
||||
|
||||
server_obj = self.servers.get_server_obj(server.get("server_id"))
|
||||
|
||||
# reset executable path
|
||||
if current_master in server["executable"]:
|
||||
server_obj.executable = str(server["executable"]).replace(
|
||||
current_master, new_local_server_path
|
||||
)
|
||||
# reset run command path
|
||||
if current_master in server["execution_command"]:
|
||||
server_obj.execution_command = str(server["execution_command"]).replace(
|
||||
current_master, new_local_server_path
|
||||
)
|
||||
# reset log path
|
||||
if current_master in server["log_path"]:
|
||||
server_obj.log_path = str(server["log_path"]).replace(
|
||||
current_master, new_local_server_path
|
||||
)
|
||||
server_obj.path = new_local_server_path
|
||||
failed = False
|
||||
for s in self.servers.failed_servers:
|
||||
if int(s["server_id"]) == int(server.get("server_id")):
|
||||
failed = True
|
||||
if not failed:
|
||||
self.servers.update_server(server_obj)
|
||||
else:
|
||||
self.servers.update_unloaded_server(server_obj)
|
||||
self.servers.init_all_servers()
|
||||
self.helper.dir_migration = False
|
||||
self.helper.websocket_helper.broadcast_page(
|
||||
"/panel/panel_config",
|
||||
"move_status",
|
||||
"done",
|
||||
)
|
||||
|
@ -12,6 +12,8 @@ import html
|
||||
import urllib.request
|
||||
import glob
|
||||
|
||||
from zoneinfo import ZoneInfo
|
||||
|
||||
# TZLocal is set as a hidden import on win pipeline
|
||||
from tzlocal import get_localzone
|
||||
from tzlocal.utils import ZoneInfoNotFoundError
|
||||
@ -136,9 +138,12 @@ class ServerInstance:
|
||||
logger.error(
|
||||
"Could not capture time zone from system. Falling back to Europe/London"
|
||||
)
|
||||
self.tz = "Europe/London"
|
||||
self.tz = ZoneInfo("Europe/London")
|
||||
self.server_scheduler = BackgroundScheduler(timezone=str(self.tz))
|
||||
self.dir_scheduler = BackgroundScheduler(timezone=str(self.tz))
|
||||
self.server_scheduler.start()
|
||||
self.dir_scheduler.start()
|
||||
self.start_dir_calc_task()
|
||||
self.backup_thread = threading.Thread(
|
||||
target=self.a_backup_server, daemon=True, name=f"backup_{self.name}"
|
||||
)
|
||||
@ -156,6 +161,8 @@ class ServerInstance:
|
||||
self.jar_update_url = server_data.executable_update_url
|
||||
self.name = server_data.server_name
|
||||
self.server_object = server_data
|
||||
self.stats_helper.select_database()
|
||||
self.reload_server_settings()
|
||||
|
||||
def reload_server_settings(self):
|
||||
server_data = HelperServers.get_server_data_by_id(self.server_id)
|
||||
@ -301,6 +308,22 @@ class ServerInstance:
|
||||
else:
|
||||
user_lang = HelperUsers.get_user_lang_by_id(user_id)
|
||||
|
||||
# Checks if user is currently attempting to move global server
|
||||
# dir
|
||||
if self.helper.dir_migration:
|
||||
self.helper.websocket_helper.broadcast_user(
|
||||
user_id,
|
||||
"send_start_error",
|
||||
{
|
||||
"error": self.helper.translation.translate(
|
||||
"error",
|
||||
"migration",
|
||||
user_lang,
|
||||
)
|
||||
},
|
||||
)
|
||||
return False
|
||||
|
||||
if self.stats_helper.get_import_status() and not forge_install:
|
||||
if user_id:
|
||||
self.helper.websocket_helper.broadcast_user(
|
||||
@ -443,7 +466,7 @@ class ServerInstance:
|
||||
)
|
||||
except Exception as ex:
|
||||
# Checks for java on initial fail
|
||||
if os.system("java -version") == 32512:
|
||||
if not self.helper.detect_java():
|
||||
if user_id:
|
||||
self.helper.websocket_helper.broadcast_user(
|
||||
user_id,
|
||||
@ -588,7 +611,6 @@ class ServerInstance:
|
||||
# We need to grab the exact forge version number.
|
||||
# We know we can find it here in the run.sh/bat script.
|
||||
try:
|
||||
|
||||
# Getting the forge version from the executable command
|
||||
version = re.findall(
|
||||
r"forge-([0-9\.]+)((?:)|(?:-([0-9\.]+)-[a-zA-Z]+)).jar",
|
||||
@ -672,7 +694,7 @@ class ServerInstance:
|
||||
execution_command = (
|
||||
f"java @{server_command[0]}"
|
||||
f" @{executable_path}{server_command[3]} nogui"
|
||||
" {server_command[4]}"
|
||||
f" {server_command[4]}"
|
||||
)
|
||||
server_obj.execution_command = execution_command
|
||||
Console.debug("SUCCESS! Forge install completed")
|
||||
@ -730,26 +752,26 @@ class ServerInstance:
|
||||
self.server_thread.join()
|
||||
|
||||
def stop_server(self):
|
||||
if self.settings["stop_command"]:
|
||||
self.send_command(self.settings["stop_command"])
|
||||
if self.settings["crash_detection"]:
|
||||
# remove crash detection watcher
|
||||
logger.info(f"Removing crash watcher for server {self.name}")
|
||||
try:
|
||||
self.server_scheduler.remove_job("c_" + str(self.server_id))
|
||||
except:
|
||||
logger.error(
|
||||
f"Removing crash watcher for server {self.name} failed. "
|
||||
f"Assuming it was never started."
|
||||
)
|
||||
else:
|
||||
# windows will need to be handled separately for Ctrl+C
|
||||
self.process.terminate()
|
||||
running = self.check_running()
|
||||
if not running:
|
||||
logger.info(f"Can't stop server {self.name} if it's not running")
|
||||
Console.info(f"Can't stop server {self.name} if it's not running")
|
||||
return
|
||||
if self.settings["crash_detection"]:
|
||||
# remove crash detection watcher
|
||||
logger.info(f"Removing crash watcher for server {self.name}")
|
||||
try:
|
||||
self.server_scheduler.remove_job("c_" + str(self.server_id))
|
||||
except:
|
||||
logger.error(
|
||||
f"Removing crash watcher for server {self.name} failed. "
|
||||
f"Assuming it was never started."
|
||||
)
|
||||
if self.settings["stop_command"]:
|
||||
self.send_command(self.settings["stop_command"])
|
||||
else:
|
||||
# windows will need to be handled separately for Ctrl+C
|
||||
self.process.terminate()
|
||||
i = 0
|
||||
|
||||
# caching the name and pid number
|
||||
@ -848,7 +870,6 @@ class ServerInstance:
|
||||
return True
|
||||
|
||||
def crash_detected(self, name):
|
||||
|
||||
# clear the old scheduled watcher task
|
||||
self.server_scheduler.remove_job(f"c_{self.server_id}")
|
||||
# remove the stats polling job since server is stopped
|
||||
@ -910,7 +931,6 @@ class ServerInstance:
|
||||
return self.process.pid if self.process is not None else None
|
||||
|
||||
def detect_crash(self):
|
||||
|
||||
logger.info(f"Detecting possible crash for server: {self.name} ")
|
||||
|
||||
running = self.check_running()
|
||||
@ -919,7 +939,7 @@ class ServerInstance:
|
||||
if running:
|
||||
return
|
||||
# check the exit code -- This could be a fix for /stop
|
||||
if self.process.returncode == 0:
|
||||
if str(self.process.returncode) in self.settings["ignored_exits"].split(","):
|
||||
logger.warning(
|
||||
f"Process {self.process.pid} exited with code "
|
||||
f"{self.process.returncode}. This is considered a clean exit"
|
||||
@ -933,7 +953,6 @@ class ServerInstance:
|
||||
self.stats_helper.sever_crashed()
|
||||
# if we haven't tried to restart more 3 or more times
|
||||
if self.restart_count <= 3:
|
||||
|
||||
# start the server if needed
|
||||
server_restarted = self.crash_detected(self.name)
|
||||
|
||||
@ -1301,7 +1320,7 @@ class ServerInstance:
|
||||
unzip_path = os.path.join(self.settings["path"], "bedrock_server.zip")
|
||||
unzip_path = self.helper.wtol_path(unzip_path)
|
||||
# unzips archive that was downloaded.
|
||||
FileHelpers.unzip_file(unzip_path)
|
||||
FileHelpers.unzip_file(unzip_path, server_update=True)
|
||||
# adjusts permissions for execution if os is not windows
|
||||
if not self.helper.is_os_windows():
|
||||
os.chmod(
|
||||
@ -1315,6 +1334,7 @@ class ServerInstance:
|
||||
logger.critical(
|
||||
f"Failed to download bedrock executable for update \n{e}"
|
||||
)
|
||||
downloaded = False
|
||||
|
||||
if downloaded:
|
||||
logger.info("Executable updated successfully. Starting Server")
|
||||
@ -1374,6 +1394,20 @@ class ServerInstance:
|
||||
for user in server_users:
|
||||
self.helper.websocket_helper.broadcast_user(user, "remove_spinner", {})
|
||||
|
||||
def start_dir_calc_task(self):
|
||||
server_dt = HelperServers.get_server_data_by_id(self.server_id)
|
||||
self.server_size = self.stats.get_server_dir_size(server_dt["path"])
|
||||
self.dir_scheduler.add_job(
|
||||
self.calc_dir_size,
|
||||
"interval",
|
||||
minutes=self.helper.get_setting("dir_size_poll_freq_minutes"),
|
||||
id=str(self.server_id) + "_dir_poll",
|
||||
)
|
||||
|
||||
def calc_dir_size(self):
|
||||
server_dt = HelperServers.get_server_data_by_id(self.server_id)
|
||||
self.server_size = self.stats.get_server_dir_size(server_dt["path"])
|
||||
|
||||
# **********************************************************************************
|
||||
# Minecraft Servers Statistics
|
||||
# **********************************************************************************
|
||||
@ -1459,7 +1493,6 @@ class ServerInstance:
|
||||
Console.critical("Can't broadcast server status to websocket")
|
||||
|
||||
def get_servers_stats(self):
|
||||
|
||||
server_stats = {}
|
||||
|
||||
logger.info("Getting Stats for Server " + self.name + " ...")
|
||||
@ -1472,9 +1505,6 @@ class ServerInstance:
|
||||
# get our server object, settings and data dictionaries
|
||||
self.reload_server_settings()
|
||||
|
||||
# world data
|
||||
server_path = server["path"]
|
||||
|
||||
# process stats
|
||||
p_stats = Stats._try_get_process_stats(self.process, self.check_running())
|
||||
|
||||
@ -1515,7 +1545,7 @@ class ServerInstance:
|
||||
"mem": p_stats.get("memory_usage", 0),
|
||||
"mem_percent": p_stats.get("mem_percentage", 0),
|
||||
"world_name": server_name,
|
||||
"world_size": Stats.get_world_size(server_path),
|
||||
"world_size": self.server_size,
|
||||
"server_port": server_port,
|
||||
"int_ping_results": int_data,
|
||||
"online": ping_data.get("online", False),
|
||||
@ -1533,7 +1563,7 @@ class ServerInstance:
|
||||
"mem": p_stats.get("memory_usage", 0),
|
||||
"mem_percent": p_stats.get("mem_percentage", 0),
|
||||
"world_name": server_name,
|
||||
"world_size": Stats.get_world_size(server_path),
|
||||
"world_size": self.server_size,
|
||||
"server_port": server_port,
|
||||
"int_ping_results": int_data,
|
||||
"online": False,
|
||||
@ -1546,7 +1576,6 @@ class ServerInstance:
|
||||
return server_stats
|
||||
|
||||
def get_server_players(self):
|
||||
|
||||
server = HelperServers.get_server_data_by_id(self.server_id)
|
||||
|
||||
logger.info(f"Getting players for server {server}")
|
||||
@ -1567,7 +1596,6 @@ class ServerInstance:
|
||||
return []
|
||||
|
||||
def get_raw_server_stats(self, server_id):
|
||||
|
||||
try:
|
||||
server = HelperServers.get_server_obj(server_id)
|
||||
except:
|
||||
@ -1603,7 +1631,6 @@ class ServerInstance:
|
||||
|
||||
# world data
|
||||
server_name = server_dt["server_name"]
|
||||
server_path = server_dt["path"]
|
||||
|
||||
# process stats
|
||||
p_stats = Stats._try_get_process_stats(self.process, self.check_running())
|
||||
@ -1636,7 +1663,7 @@ class ServerInstance:
|
||||
"mem": p_stats.get("memory_usage", 0),
|
||||
"mem_percent": p_stats.get("mem_percentage", 0),
|
||||
"world_name": server_name,
|
||||
"world_size": Stats.get_world_size(server_path),
|
||||
"world_size": self.server_size,
|
||||
"server_port": server_port,
|
||||
"int_ping_results": int_data,
|
||||
"online": ping_data.get("online", False),
|
||||
@ -1665,7 +1692,7 @@ class ServerInstance:
|
||||
"mem": p_stats.get("memory_usage", 0),
|
||||
"mem_percent": p_stats.get("mem_percentage", 0),
|
||||
"world_name": server_name,
|
||||
"world_size": Stats.get_world_size(server_path),
|
||||
"world_size": self.server_size,
|
||||
"server_port": server_port,
|
||||
"int_ping_results": int_data,
|
||||
"online": ping_data["online"],
|
||||
@ -1684,7 +1711,7 @@ class ServerInstance:
|
||||
"mem": p_stats.get("memory_usage", 0),
|
||||
"mem_percent": p_stats.get("mem_percentage", 0),
|
||||
"world_name": server_name,
|
||||
"world_size": Stats.get_world_size(server_path),
|
||||
"world_size": self.server_size,
|
||||
"server_port": server_port,
|
||||
"int_ping_results": int_data,
|
||||
"online": False,
|
||||
@ -1703,7 +1730,7 @@ class ServerInstance:
|
||||
"mem": p_stats.get("memory_usage", 0),
|
||||
"mem_percent": p_stats.get("mem_percentage", 0),
|
||||
"world_name": server_name,
|
||||
"world_size": Stats.get_world_size(server_path),
|
||||
"world_size": self.server_size,
|
||||
"server_port": server_port,
|
||||
"int_ping_results": int_data,
|
||||
"online": False,
|
||||
@ -1716,7 +1743,6 @@ class ServerInstance:
|
||||
return server_stats
|
||||
|
||||
def record_server_stats(self):
|
||||
|
||||
server_stats = self.get_servers_stats()
|
||||
self.stats_helper.insert_server_stats(server_stats)
|
||||
|
||||
|
@ -4,6 +4,7 @@ import logging
|
||||
import threading
|
||||
import asyncio
|
||||
import datetime
|
||||
import json
|
||||
|
||||
from tzlocal import get_localzone
|
||||
from tzlocal.utils import ZoneInfoNotFoundError
|
||||
@ -635,7 +636,9 @@ class TasksManager:
|
||||
logger.error(f"Task failed with error: {event.exception}")
|
||||
|
||||
def start_stats_recording(self):
|
||||
stats_update_frequency = self.helper.get_setting("stats_update_frequency")
|
||||
stats_update_frequency = self.helper.get_setting(
|
||||
"stats_update_frequency_seconds"
|
||||
)
|
||||
logger.info(
|
||||
f"Stats collection frequency set to {stats_update_frequency} seconds"
|
||||
)
|
||||
@ -672,7 +675,6 @@ class TasksManager:
|
||||
host_stats = HelpersManagement.get_latest_hosts_stats()
|
||||
|
||||
while True:
|
||||
|
||||
if host_stats.get(
|
||||
"cpu_usage"
|
||||
) != HelpersManagement.get_latest_hosts_stats().get(
|
||||
@ -687,18 +689,37 @@ class TasksManager:
|
||||
host_stats = HelpersManagement.get_latest_hosts_stats()
|
||||
if len(self.helper.websocket_helper.clients) > 0:
|
||||
# There are clients
|
||||
self.helper.websocket_helper.broadcast_page(
|
||||
"/panel/dashboard",
|
||||
"update_host_stats",
|
||||
{
|
||||
"cpu_usage": host_stats.get("cpu_usage"),
|
||||
"cpu_cores": host_stats.get("cpu_cores"),
|
||||
"cpu_cur_freq": host_stats.get("cpu_cur_freq"),
|
||||
"cpu_max_freq": host_stats.get("cpu_max_freq"),
|
||||
"mem_percent": host_stats.get("mem_percent"),
|
||||
"mem_usage": host_stats.get("mem_usage"),
|
||||
},
|
||||
)
|
||||
try:
|
||||
self.helper.websocket_helper.broadcast_page(
|
||||
"/panel/dashboard",
|
||||
"update_host_stats",
|
||||
{
|
||||
"cpu_usage": host_stats.get("cpu_usage"),
|
||||
"cpu_cores": host_stats.get("cpu_cores"),
|
||||
"cpu_cur_freq": host_stats.get("cpu_cur_freq"),
|
||||
"cpu_max_freq": host_stats.get("cpu_max_freq"),
|
||||
"mem_percent": host_stats.get("mem_percent"),
|
||||
"mem_usage": host_stats.get("mem_usage"),
|
||||
"disk_usage": json.loads(
|
||||
host_stats.get("disk_json").replace("'", '"')
|
||||
),
|
||||
"mounts": self.helper.get_setting("monitored_mounts"),
|
||||
},
|
||||
)
|
||||
except:
|
||||
self.helper.websocket_helper.broadcast_page(
|
||||
"/panel/dashboard",
|
||||
"update_host_stats",
|
||||
{
|
||||
"cpu_usage": host_stats.get("cpu_usage"),
|
||||
"cpu_cores": host_stats.get("cpu_cores"),
|
||||
"cpu_cur_freq": host_stats.get("cpu_cur_freq"),
|
||||
"cpu_max_freq": host_stats.get("cpu_max_freq"),
|
||||
"mem_percent": host_stats.get("mem_percent"),
|
||||
"mem_usage": host_stats.get("mem_usage"),
|
||||
"disk_usage": {},
|
||||
},
|
||||
)
|
||||
time.sleep(1)
|
||||
|
||||
def check_for_updates(self):
|
||||
@ -730,10 +751,42 @@ class TasksManager:
|
||||
logger.debug("Could not clear out file from import directory")
|
||||
|
||||
def log_watcher(self):
|
||||
self.controller.servers.check_for_old_logs()
|
||||
self.check_for_old_logs()
|
||||
self.scheduler.add_job(
|
||||
self.controller.servers.check_for_old_logs,
|
||||
self.check_for_old_logs,
|
||||
"interval",
|
||||
hours=6,
|
||||
id="log-mgmt",
|
||||
)
|
||||
|
||||
def check_for_old_logs(self):
|
||||
# check for server logs first
|
||||
self.controller.servers.check_for_old_logs()
|
||||
# check for crafty logs now
|
||||
logs_path = os.path.join(self.controller.project_root, "logs")
|
||||
logs_delete_after = int(
|
||||
self.helper.get_setting("crafty_logs_delete_after_days")
|
||||
)
|
||||
latest_log_files = [
|
||||
"session.log",
|
||||
"schedule.log",
|
||||
"tornado-access.log",
|
||||
"session.log",
|
||||
"commander.log",
|
||||
]
|
||||
# we won't delete if delete logs after is set to 0
|
||||
if logs_delete_after != 0:
|
||||
log_files = list(
|
||||
filter(
|
||||
lambda val: val not in latest_log_files,
|
||||
os.listdir(logs_path),
|
||||
)
|
||||
)
|
||||
for log_file in log_files:
|
||||
log_file_path = os.path.join(logs_path, log_file)
|
||||
if Helpers.check_file_exists(
|
||||
log_file_path
|
||||
) and Helpers.is_file_older_than_x_days(
|
||||
log_file_path, logs_delete_after
|
||||
):
|
||||
os.remove(log_file_path)
|
||||
|
@ -575,6 +575,33 @@ class AjaxHandler(BaseHandler):
|
||||
self.controller.server_jars.manual_refresh_cache()
|
||||
return
|
||||
|
||||
elif page == "update_server_dir":
|
||||
if self.helper.dir_migration:
|
||||
return
|
||||
for server in self.controller.servers.get_all_servers_stats():
|
||||
if server["stats"]["running"]:
|
||||
self.helper.websocket_helper.broadcast_user(
|
||||
exec_user["user_id"],
|
||||
"send_start_error",
|
||||
{
|
||||
"error": "You must stop all servers before "
|
||||
"starting a storage migration."
|
||||
},
|
||||
)
|
||||
return
|
||||
if not superuser:
|
||||
self.redirect("/panel/error?error=Not a super user")
|
||||
return
|
||||
if self.helper.is_env_docker():
|
||||
self.redirect(
|
||||
"/panel/error?error=This feature is not"
|
||||
" supported on docker environments"
|
||||
)
|
||||
return
|
||||
new_dir = urllib.parse.unquote(self.get_argument("server_dir"))
|
||||
self.controller.update_master_server_dir(new_dir, exec_user["user_id"])
|
||||
return
|
||||
|
||||
@tornado.web.authenticated
|
||||
def delete(self, page):
|
||||
api_key, _, exec_user = self.current_user
|
||||
|
@ -6,7 +6,6 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class DefaultHandler(BaseHandler):
|
||||
|
||||
# Override prepare() instead of get() to cover all possible HTTP methods.
|
||||
def prepare(self, page=None): # pylint: disable=arguments-differ
|
||||
if page is not None:
|
||||
|
@ -290,9 +290,11 @@ class PanelHandler(BaseHandler):
|
||||
page_data: t.Dict[str, t.Any] = {
|
||||
# todo: make this actually pull and compare version data
|
||||
"update_available": self.helper.update_available,
|
||||
"docker": self.helper.is_env_docker(),
|
||||
"background": self.controller.cached_login,
|
||||
"login_opacity": self.controller.management.get_login_opacity(),
|
||||
"serverTZ": tz,
|
||||
"monitored": self.helper.get_setting("monitored_mounts"),
|
||||
"version_data": self.helper.get_version_string(),
|
||||
"failed_servers": self.controller.servers.failed_servers,
|
||||
"user_data": exec_user,
|
||||
@ -332,7 +334,12 @@ class PanelHandler(BaseHandler):
|
||||
else None,
|
||||
"superuser": superuser,
|
||||
}
|
||||
|
||||
try:
|
||||
page_data["hosts_data"]["disk_json"] = json.loads(
|
||||
page_data["hosts_data"]["disk_json"].replace("'", '"')
|
||||
)
|
||||
except:
|
||||
page_data["hosts_data"]["disk_json"] = {}
|
||||
if page == "unauthorized":
|
||||
template = "panel/denied.html"
|
||||
|
||||
@ -532,6 +539,7 @@ class PanelHandler(BaseHandler):
|
||||
"auto_start": server_temp_obj["auto_start"],
|
||||
"crash_detection": server_temp_obj["crash_detection"],
|
||||
"show_status": server_temp_obj["show_status"],
|
||||
"ignored_exits": server_temp_obj["ignored_exits"],
|
||||
},
|
||||
"running": False,
|
||||
"crashed": False,
|
||||
@ -841,6 +849,9 @@ class PanelHandler(BaseHandler):
|
||||
page_data["auth-servers"] = auth_servers
|
||||
page_data["role-servers"] = auth_role_servers
|
||||
page_data["user-roles"] = user_roles
|
||||
page_data["servers_dir"], _tail = os.path.split(
|
||||
self.controller.management.get_master_server_dir()
|
||||
)
|
||||
|
||||
page_data["users"] = self.controller.users.user_query(exec_user["user_id"])
|
||||
page_data["roles"] = self.controller.users.user_role_query(
|
||||
@ -880,6 +891,7 @@ class PanelHandler(BaseHandler):
|
||||
page_data["config-json"] = data
|
||||
page_data["availables_languages"] = []
|
||||
page_data["all_languages"] = []
|
||||
page_data["all_partitions"] = self.helper.get_all_mounts()
|
||||
|
||||
for file in sorted(
|
||||
os.listdir(
|
||||
@ -1585,6 +1597,8 @@ class PanelHandler(BaseHandler):
|
||||
crash_detection = int(float(self.get_argument("crash_detection", "0")))
|
||||
logs_delete_after = int(float(self.get_argument("logs_delete_after", "0")))
|
||||
java_selection = self.get_argument("java_selection", None)
|
||||
# make sure there is no whitespace
|
||||
ignored_exits = self.get_argument("ignored_exits", "").replace(" ", "")
|
||||
# subpage = self.get_argument('subpage', None)
|
||||
|
||||
server_id = self.check_server_id()
|
||||
@ -1669,6 +1683,7 @@ class PanelHandler(BaseHandler):
|
||||
server_obj.auto_start = auto_start
|
||||
server_obj.crash_detection = crash_detection
|
||||
server_obj.logs_delete_after = logs_delete_after
|
||||
server_obj.ignored_exits = ignored_exits
|
||||
failed = False
|
||||
for servers in self.controller.servers.failed_servers:
|
||||
if servers["server_id"] == int(server_id):
|
||||
|
@ -10,7 +10,6 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
class PublicHandler(BaseHandler):
|
||||
def set_current_user(self, user_id: str = None):
|
||||
|
||||
expire_days = self.helper.get_setting("cookie_expire")
|
||||
|
||||
# if helper comes back with false
|
||||
@ -29,7 +28,6 @@ class PublicHandler(BaseHandler):
|
||||
# self.clear_cookie("user_data")
|
||||
|
||||
def get(self, page=None):
|
||||
|
||||
error = bleach.clean(self.get_argument("error", "Invalid Login!"))
|
||||
error_msg = bleach.clean(self.get_argument("error_msg", ""))
|
||||
|
||||
@ -81,7 +79,6 @@ class PublicHandler(BaseHandler):
|
||||
)
|
||||
|
||||
def post(self, page=None):
|
||||
|
||||
error = bleach.clean(self.get_argument("error", "Invalid Login!"))
|
||||
error_msg = bleach.clean(self.get_argument("error_msg", ""))
|
||||
|
||||
@ -96,7 +93,6 @@ class PublicHandler(BaseHandler):
|
||||
page_data["query"] = self.request.query
|
||||
|
||||
if page == "login":
|
||||
|
||||
next_page = "/login"
|
||||
if self.request.query:
|
||||
next_page = "/login?" + self.request.query
|
||||
|
@ -26,7 +26,6 @@ login_schema = {
|
||||
|
||||
class ApiAuthLoginHandler(BaseApiHandler):
|
||||
def post(self):
|
||||
|
||||
try:
|
||||
data = json.loads(self.request.body)
|
||||
except json.decoder.JSONDecodeError as e:
|
||||
|
@ -631,7 +631,6 @@ class ApiServersIndexHandler(BaseApiHandler):
|
||||
self.finish_json(200, {"status": "ok", "data": auth_data[0]})
|
||||
|
||||
def post(self):
|
||||
|
||||
auth_data = self.authenticate_user()
|
||||
if not auth_data:
|
||||
return
|
||||
|
@ -183,6 +183,7 @@ class ServerHandler(BaseHandler):
|
||||
"version_data": "version_data_here", # TODO
|
||||
"user_data": exec_user,
|
||||
"show_contribute": self.helper.get_setting("show_contribute_link", True),
|
||||
"background": self.controller.cached_login,
|
||||
"lang": self.controller.users.get_user_lang_by_id(exec_user["user_id"]),
|
||||
"lang_page": Helpers.get_lang_page(
|
||||
self.controller.users.get_user_lang_by_id(exec_user["user_id"])
|
||||
@ -403,6 +404,14 @@ class ServerHandler(BaseHandler):
|
||||
jar_type, server_type, server_version = server_parts
|
||||
# TODO: add server type check here and call the correct server
|
||||
# add functions if not a jar
|
||||
if server_type == "forge" and not self.helper.detect_java():
|
||||
translation = self.helper.translation.translate(
|
||||
"error",
|
||||
"installerJava",
|
||||
self.controller.users.get_user_lang_by_id(exec_user["user_id"]),
|
||||
).format(server_name)
|
||||
self.redirect(f"/panel/error?error={translation}")
|
||||
return
|
||||
new_server_id = self.controller.create_jar_server(
|
||||
jar_type,
|
||||
server_type,
|
||||
@ -551,7 +560,6 @@ class ServerHandler(BaseHandler):
|
||||
self.get_remote_ip(),
|
||||
)
|
||||
else:
|
||||
|
||||
new_server_id = self.controller.create_bedrock_server(
|
||||
server_name,
|
||||
exec_user["user_id"],
|
||||
|
@ -7,12 +7,12 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
class StatusHandler(BaseHandler):
|
||||
def get(self):
|
||||
page_data = {"background": self.controller.cached_login}
|
||||
page_data["lang"] = self.helper.get_setting("language")
|
||||
page_data["lang_page"] = self.helper.get_lang_page(
|
||||
self.helper.get_setting("language")
|
||||
)
|
||||
page_data["servers"] = self.controller.servers.get_all_servers_stats()
|
||||
page_data = {
|
||||
"background": self.controller.cached_login,
|
||||
"lang": self.helper.get_setting("language"),
|
||||
"lang_page": self.helper.get_lang_page(self.helper.get_setting("language")),
|
||||
"servers": self.controller.servers.get_all_servers_stats(),
|
||||
}
|
||||
running = 0
|
||||
for srv in page_data["servers"]:
|
||||
if srv["stats"]["running"]:
|
||||
|
@ -59,7 +59,6 @@ class Webserver:
|
||||
|
||||
@staticmethod
|
||||
def log_function(handler):
|
||||
|
||||
info = {
|
||||
"Status_Code": handler.get_status(),
|
||||
"Method": handler.request.method,
|
||||
@ -103,7 +102,6 @@ class Webserver:
|
||||
logger.debug("Applied asyncio patch")
|
||||
|
||||
def run_tornado(self):
|
||||
|
||||
# let's verify we have an SSL cert
|
||||
self.helper.create_self_signed_cert()
|
||||
|
||||
|
@ -18,7 +18,6 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
@tornado.web.stream_request_body
|
||||
class UploadHandler(BaseHandler):
|
||||
|
||||
# noinspection PyAttributeOutsideInit
|
||||
def initialize(
|
||||
self,
|
||||
@ -52,18 +51,19 @@ class UploadHandler(BaseHandler):
|
||||
f"User with ID {user_id} attempted to upload a file that"
|
||||
f" exceeded the max body size."
|
||||
)
|
||||
self.helper.websocket_helper.broadcast_user(
|
||||
user_id,
|
||||
"send_start_error",
|
||||
|
||||
return self.finish_json(
|
||||
413,
|
||||
{
|
||||
"error": self.helper.translation.translate(
|
||||
"status": "error",
|
||||
"error": "TOO LARGE",
|
||||
"info": self.helper.translation.translate(
|
||||
"error",
|
||||
"fileTooLarge",
|
||||
self.controller.users.get_user_lang_by_id(user_id),
|
||||
),
|
||||
},
|
||||
)
|
||||
return
|
||||
self.do_upload = True
|
||||
|
||||
if superuser:
|
||||
@ -141,48 +141,49 @@ class UploadHandler(BaseHandler):
|
||||
f"User with ID {user_id} attempted to upload a file that"
|
||||
f" exceeded the max body size."
|
||||
)
|
||||
self.helper.websocket_helper.broadcast_user(
|
||||
user_id,
|
||||
"send_start_error",
|
||||
|
||||
return self.finish_json(
|
||||
413,
|
||||
{
|
||||
"error": self.helper.translation.translate(
|
||||
"status": "error",
|
||||
"error": "TOO LARGE",
|
||||
"info": self.helper.translation.translate(
|
||||
"error",
|
||||
"fileTooLarge",
|
||||
self.controller.users.get_user_lang_by_id(user_id),
|
||||
),
|
||||
},
|
||||
)
|
||||
return
|
||||
self.do_upload = True
|
||||
|
||||
if not superuser:
|
||||
self.helper.websocket_helper.broadcast_user(
|
||||
user_id,
|
||||
"send_start_error",
|
||||
return self.finish_json(
|
||||
401,
|
||||
{
|
||||
"error": self.helper.translation.translate(
|
||||
"status": "error",
|
||||
"error": "UNAUTHORIZED ACCESS",
|
||||
"info": self.helper.translation.translate(
|
||||
"error",
|
||||
"superError",
|
||||
self.controller.users.get_user_lang_by_id(user_id),
|
||||
),
|
||||
},
|
||||
)
|
||||
return
|
||||
if not self.request.headers.get("X-Content-Type", None).startswith(
|
||||
"image/"
|
||||
):
|
||||
self.helper.websocket_helper.broadcast_user(
|
||||
user_id,
|
||||
"send_start_error",
|
||||
return self.finish_json(
|
||||
415,
|
||||
{
|
||||
"error": self.helper.translation.translate(
|
||||
"status": "error",
|
||||
"error": "TYPE ERROR",
|
||||
"info": self.helper.translation.translate(
|
||||
"error",
|
||||
"fileError",
|
||||
self.controller.users.get_user_lang_by_id(user_id),
|
||||
),
|
||||
},
|
||||
)
|
||||
return
|
||||
if user_id is None:
|
||||
logger.warning("User ID not found in upload handler call")
|
||||
Console.warning("User ID not found in upload handler call")
|
||||
@ -219,18 +220,19 @@ class UploadHandler(BaseHandler):
|
||||
f"User with ID {user_id} attempted to upload a file that"
|
||||
f" exceeded the max body size."
|
||||
)
|
||||
self.helper.websocket_helper.broadcast_user(
|
||||
user_id,
|
||||
"send_start_error",
|
||||
|
||||
return self.finish_json(
|
||||
413,
|
||||
{
|
||||
"error": self.helper.translation.translate(
|
||||
"status": "error",
|
||||
"error": "TOO LARGE",
|
||||
"info": self.helper.translation.translate(
|
||||
"error",
|
||||
"fileTooLarge",
|
||||
self.controller.users.get_user_lang_by_id(user_id),
|
||||
),
|
||||
},
|
||||
)
|
||||
return
|
||||
self.do_upload = True
|
||||
|
||||
if superuser:
|
||||
|
@ -79,7 +79,6 @@ class SocketHandler(tornado.websocket.WebSocketHandler):
|
||||
# pylint: disable=arguments-renamed
|
||||
@staticmethod
|
||||
def on_message(raw_message):
|
||||
|
||||
logger.debug(f"Got message from WebSocket connection {raw_message}")
|
||||
message = json.loads(raw_message)
|
||||
logger.debug(f"Event Type: {message['event']}, Data: {message['data']}")
|
||||
|
@ -1,5 +1,5 @@
|
||||
{
|
||||
"major": 4,
|
||||
"minor": 0,
|
||||
"sub": 20
|
||||
"sub": 21
|
||||
}
|
||||
|
12749
app/frontend/static/assets/vendors/fontawesome5/css/all.css
vendored
12749
app/frontend/static/assets/vendors/fontawesome5/css/all.css
vendored
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
@ -1,15 +0,0 @@
|
||||
/*!
|
||||
* Font Awesome Pro 5.14.0 by @fontawesome - https://fontawesome.com
|
||||
* License - https://fontawesome.com/license (Commercial License)
|
||||
*/
|
||||
@font-face {
|
||||
font-family: 'Font Awesome 5 Brands';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: block;
|
||||
src: url("../webfonts/fa-brands-400.eot");
|
||||
src: url("../webfonts/fa-brands-400.eot?#iefix") format("embedded-opentype"), url("../webfonts/fa-brands-400.woff2") format("woff2"), url("../webfonts/fa-brands-400.woff") format("woff"), url("../webfonts/fa-brands-400.ttf") format("truetype"), url("../webfonts/fa-brands-400.svg#fontawesome") format("svg"); }
|
||||
|
||||
.fab {
|
||||
font-family: 'Font Awesome 5 Brands';
|
||||
font-weight: 400; }
|
@ -1,5 +0,0 @@
|
||||
/*!
|
||||
* Font Awesome Pro 5.14.0 by @fontawesome - https://fontawesome.com
|
||||
* License - https://fontawesome.com/license (Commercial License)
|
||||
*/
|
||||
@font-face{font-family:"Font Awesome 5 Brands";font-style:normal;font-weight:400;font-display:block;src:url(../webfonts/fa-brands-400.eot);src:url(../webfonts/fa-brands-400.eot?#iefix) format("embedded-opentype"),url(../webfonts/fa-brands-400.woff2) format("woff2"),url(../webfonts/fa-brands-400.woff) format("woff"),url(../webfonts/fa-brands-400.ttf) format("truetype"),url(../webfonts/fa-brands-400.svg#fontawesome) format("svg")}.fab{font-family:"Font Awesome 5 Brands";font-weight:400}
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
@ -1,15 +0,0 @@
|
||||
/*!
|
||||
* Font Awesome Pro 5.14.0 by @fontawesome - https://fontawesome.com
|
||||
* License - https://fontawesome.com/license (Commercial License)
|
||||
*/
|
||||
@font-face {
|
||||
font-family: 'Font Awesome 5 Pro';
|
||||
font-style: normal;
|
||||
font-weight: 300;
|
||||
font-display: block;
|
||||
src: url("../webfonts/fa-light-300.eot");
|
||||
src: url("../webfonts/fa-light-300.eot?#iefix") format("embedded-opentype"), url("../webfonts/fa-light-300.woff2") format("woff2"), url("../webfonts/fa-light-300.woff") format("woff"), url("../webfonts/fa-light-300.ttf") format("truetype"), url("../webfonts/fa-light-300.svg#fontawesome") format("svg"); }
|
||||
|
||||
.fal {
|
||||
font-family: 'Font Awesome 5 Pro';
|
||||
font-weight: 300; }
|
@ -1,5 +0,0 @@
|
||||
/*!
|
||||
* Font Awesome Pro 5.14.0 by @fontawesome - https://fontawesome.com
|
||||
* License - https://fontawesome.com/license (Commercial License)
|
||||
*/
|
||||
@font-face{font-family:"Font Awesome 5 Pro";font-style:normal;font-weight:300;font-display:block;src:url(../webfonts/fa-light-300.eot);src:url(../webfonts/fa-light-300.eot?#iefix) format("embedded-opentype"),url(../webfonts/fa-light-300.woff2) format("woff2"),url(../webfonts/fa-light-300.woff) format("woff"),url(../webfonts/fa-light-300.ttf) format("truetype"),url(../webfonts/fa-light-300.svg#fontawesome) format("svg")}.fal{font-family:"Font Awesome 5 Pro";font-weight:300}
|
@ -1,15 +0,0 @@
|
||||
/*!
|
||||
* Font Awesome Pro 5.14.0 by @fontawesome - https://fontawesome.com
|
||||
* License - https://fontawesome.com/license (Commercial License)
|
||||
*/
|
||||
@font-face {
|
||||
font-family: 'Font Awesome 5 Pro';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: block;
|
||||
src: url("../webfonts/fa-regular-400.eot");
|
||||
src: url("../webfonts/fa-regular-400.eot?#iefix") format("embedded-opentype"), url("../webfonts/fa-regular-400.woff2") format("woff2"), url("../webfonts/fa-regular-400.woff") format("woff"), url("../webfonts/fa-regular-400.ttf") format("truetype"), url("../webfonts/fa-regular-400.svg#fontawesome") format("svg"); }
|
||||
|
||||
.far {
|
||||
font-family: 'Font Awesome 5 Pro';
|
||||
font-weight: 400; }
|
@ -1,5 +0,0 @@
|
||||
/*!
|
||||
* Font Awesome Pro 5.14.0 by @fontawesome - https://fontawesome.com
|
||||
* License - https://fontawesome.com/license (Commercial License)
|
||||
*/
|
||||
@font-face{font-family:"Font Awesome 5 Pro";font-style:normal;font-weight:400;font-display:block;src:url(../webfonts/fa-regular-400.eot);src:url(../webfonts/fa-regular-400.eot?#iefix) format("embedded-opentype"),url(../webfonts/fa-regular-400.woff2) format("woff2"),url(../webfonts/fa-regular-400.woff) format("woff"),url(../webfonts/fa-regular-400.ttf) format("truetype"),url(../webfonts/fa-regular-400.svg#fontawesome) format("svg")}.far{font-family:"Font Awesome 5 Pro";font-weight:400}
|
@ -1,16 +0,0 @@
|
||||
/*!
|
||||
* Font Awesome Pro 5.14.0 by @fontawesome - https://fontawesome.com
|
||||
* License - https://fontawesome.com/license (Commercial License)
|
||||
*/
|
||||
@font-face {
|
||||
font-family: 'Font Awesome 5 Pro';
|
||||
font-style: normal;
|
||||
font-weight: 900;
|
||||
font-display: block;
|
||||
src: url("../webfonts/fa-solid-900.eot");
|
||||
src: url("../webfonts/fa-solid-900.eot?#iefix") format("embedded-opentype"), url("../webfonts/fa-solid-900.woff2") format("woff2"), url("../webfonts/fa-solid-900.woff") format("woff"), url("../webfonts/fa-solid-900.ttf") format("truetype"), url("../webfonts/fa-solid-900.svg#fontawesome") format("svg"); }
|
||||
|
||||
.fa,
|
||||
.fas {
|
||||
font-family: 'Font Awesome 5 Pro';
|
||||
font-weight: 900; }
|
@ -1,5 +0,0 @@
|
||||
/*!
|
||||
* Font Awesome Pro 5.14.0 by @fontawesome - https://fontawesome.com
|
||||
* License - https://fontawesome.com/license (Commercial License)
|
||||
*/
|
||||
@font-face{font-family:"Font Awesome 5 Pro";font-style:normal;font-weight:900;font-display:block;src:url(../webfonts/fa-solid-900.eot);src:url(../webfonts/fa-solid-900.eot?#iefix) format("embedded-opentype"),url(../webfonts/fa-solid-900.woff2) format("woff2"),url(../webfonts/fa-solid-900.woff) format("woff"),url(../webfonts/fa-solid-900.ttf) format("truetype"),url(../webfonts/fa-solid-900.svg#fontawesome) format("svg")}.fa,.fas{font-family:"Font Awesome 5 Pro";font-weight:900}
|
@ -1,492 +0,0 @@
|
||||
/*!
|
||||
* Font Awesome Pro 5.14.0 by @fontawesome - https://fontawesome.com
|
||||
* License - https://fontawesome.com/license (Commercial License)
|
||||
*/
|
||||
svg:not(:root).svg-inline--fa {
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
.svg-inline--fa {
|
||||
display: inline-block;
|
||||
font-size: inherit;
|
||||
height: 1em;
|
||||
overflow: visible;
|
||||
vertical-align: -.125em;
|
||||
}
|
||||
|
||||
.svg-inline--fa.fa-lg {
|
||||
vertical-align: -.225em;
|
||||
}
|
||||
|
||||
.svg-inline--fa.fa-w-1 {
|
||||
width: 0.0625em;
|
||||
}
|
||||
|
||||
.svg-inline--fa.fa-w-2 {
|
||||
width: 0.125em;
|
||||
}
|
||||
|
||||
.svg-inline--fa.fa-w-3 {
|
||||
width: 0.1875em;
|
||||
}
|
||||
|
||||
.svg-inline--fa.fa-w-4 {
|
||||
width: 0.25em;
|
||||
}
|
||||
|
||||
.svg-inline--fa.fa-w-5 {
|
||||
width: 0.3125em;
|
||||
}
|
||||
|
||||
.svg-inline--fa.fa-w-6 {
|
||||
width: 0.375em;
|
||||
}
|
||||
|
||||
.svg-inline--fa.fa-w-7 {
|
||||
width: 0.4375em;
|
||||
}
|
||||
|
||||
.svg-inline--fa.fa-w-8 {
|
||||
width: 0.5em;
|
||||
}
|
||||
|
||||
.svg-inline--fa.fa-w-9 {
|
||||
width: 0.5625em;
|
||||
}
|
||||
|
||||
.svg-inline--fa.fa-w-10 {
|
||||
width: 0.625em;
|
||||
}
|
||||
|
||||
.svg-inline--fa.fa-w-11 {
|
||||
width: 0.6875em;
|
||||
}
|
||||
|
||||
.svg-inline--fa.fa-w-12 {
|
||||
width: 0.75em;
|
||||
}
|
||||
|
||||
.svg-inline--fa.fa-w-13 {
|
||||
width: 0.8125em;
|
||||
}
|
||||
|
||||
.svg-inline--fa.fa-w-14 {
|
||||
width: 0.875em;
|
||||
}
|
||||
|
||||
.svg-inline--fa.fa-w-15 {
|
||||
width: 0.9375em;
|
||||
}
|
||||
|
||||
.svg-inline--fa.fa-w-16 {
|
||||
width: 1em;
|
||||
}
|
||||
|
||||
.svg-inline--fa.fa-w-17 {
|
||||
width: 1.0625em;
|
||||
}
|
||||
|
||||
.svg-inline--fa.fa-w-18 {
|
||||
width: 1.125em;
|
||||
}
|
||||
|
||||
.svg-inline--fa.fa-w-19 {
|
||||
width: 1.1875em;
|
||||
}
|
||||
|
||||
.svg-inline--fa.fa-w-20 {
|
||||
width: 1.25em;
|
||||
}
|
||||
|
||||
.svg-inline--fa.fa-pull-left {
|
||||
margin-right: .3em;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.svg-inline--fa.fa-pull-right {
|
||||
margin-left: .3em;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.svg-inline--fa.fa-border {
|
||||
height: 1.5em;
|
||||
}
|
||||
|
||||
.svg-inline--fa.fa-li {
|
||||
width: 2em;
|
||||
}
|
||||
|
||||
.svg-inline--fa.fa-fw {
|
||||
width: 1.25em;
|
||||
}
|
||||
|
||||
.fa-layers svg.svg-inline--fa {
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
margin: auto;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.fa-layers {
|
||||
display: inline-block;
|
||||
height: 1em;
|
||||
position: relative;
|
||||
text-align: center;
|
||||
vertical-align: -.125em;
|
||||
width: 1em;
|
||||
}
|
||||
|
||||
.fa-layers svg.svg-inline--fa {
|
||||
-webkit-transform-origin: center center;
|
||||
transform-origin: center center;
|
||||
}
|
||||
|
||||
.fa-layers-text,
|
||||
.fa-layers-counter {
|
||||
display: inline-block;
|
||||
position: absolute;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.fa-layers-text {
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
-webkit-transform: translate(-50%, -50%);
|
||||
transform: translate(-50%, -50%);
|
||||
-webkit-transform-origin: center center;
|
||||
transform-origin: center center;
|
||||
}
|
||||
|
||||
.fa-layers-counter {
|
||||
background-color: #ff253a;
|
||||
border-radius: 1em;
|
||||
-webkit-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
var(--base-text);
|
||||
height: 1.5em;
|
||||
line-height: 1;
|
||||
max-width: 5em;
|
||||
min-width: 1.5em;
|
||||
overflow: hidden;
|
||||
padding: .25em;
|
||||
right: 0;
|
||||
text-overflow: ellipsis;
|
||||
top: 0;
|
||||
-webkit-transform: scale(0.25);
|
||||
transform: scale(0.25);
|
||||
-webkit-transform-origin: top right;
|
||||
transform-origin: top right;
|
||||
}
|
||||
|
||||
.fa-layers-bottom-right {
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
top: auto;
|
||||
-webkit-transform: scale(0.25);
|
||||
transform: scale(0.25);
|
||||
-webkit-transform-origin: bottom right;
|
||||
transform-origin: bottom right;
|
||||
}
|
||||
|
||||
.fa-layers-bottom-left {
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: auto;
|
||||
top: auto;
|
||||
-webkit-transform: scale(0.25);
|
||||
transform: scale(0.25);
|
||||
-webkit-transform-origin: bottom left;
|
||||
transform-origin: bottom left;
|
||||
}
|
||||
|
||||
.fa-layers-top-right {
|
||||
right: 0;
|
||||
top: 0;
|
||||
-webkit-transform: scale(0.25);
|
||||
transform: scale(0.25);
|
||||
-webkit-transform-origin: top right;
|
||||
transform-origin: top right;
|
||||
}
|
||||
|
||||
.fa-layers-top-left {
|
||||
left: 0;
|
||||
right: auto;
|
||||
top: 0;
|
||||
-webkit-transform: scale(0.25);
|
||||
transform: scale(0.25);
|
||||
-webkit-transform-origin: top left;
|
||||
transform-origin: top left;
|
||||
}
|
||||
|
||||
.fa-lg {
|
||||
font-size: 1.33333em;
|
||||
line-height: 0.75em;
|
||||
vertical-align: -.0667em;
|
||||
}
|
||||
|
||||
.fa-xs {
|
||||
font-size: .75em;
|
||||
}
|
||||
|
||||
.fa-sm {
|
||||
font-size: .875em;
|
||||
}
|
||||
|
||||
.fa-1x {
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
.fa-2x {
|
||||
font-size: 2em;
|
||||
}
|
||||
|
||||
.fa-3x {
|
||||
font-size: 3em;
|
||||
}
|
||||
|
||||
.fa-4x {
|
||||
font-size: 4em;
|
||||
}
|
||||
|
||||
.fa-5x {
|
||||
font-size: 5em;
|
||||
}
|
||||
|
||||
.fa-6x {
|
||||
font-size: 6em;
|
||||
}
|
||||
|
||||
.fa-7x {
|
||||
font-size: 7em;
|
||||
}
|
||||
|
||||
.fa-8x {
|
||||
font-size: 8em;
|
||||
}
|
||||
|
||||
.fa-9x {
|
||||
font-size: 9em;
|
||||
}
|
||||
|
||||
.fa-10x {
|
||||
font-size: 10em;
|
||||
}
|
||||
|
||||
.fa-fw {
|
||||
text-align: center;
|
||||
width: 1.25em;
|
||||
}
|
||||
|
||||
.fa-ul {
|
||||
list-style-type: none;
|
||||
margin-left: 2.5em;
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
.fa-ul>li {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.fa-li {
|
||||
left: -2em;
|
||||
position: absolute;
|
||||
text-align: center;
|
||||
width: 2em;
|
||||
line-height: inherit;
|
||||
}
|
||||
|
||||
.fa-border {
|
||||
border: solid 0.08em #eee;
|
||||
border-radius: .1em;
|
||||
padding: .2em .25em .15em;
|
||||
}
|
||||
|
||||
.fa-pull-left {
|
||||
float: left;
|
||||
}
|
||||
|
||||
.fa-pull-right {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.fa.fa-pull-left,
|
||||
.fas.fa-pull-left,
|
||||
.far.fa-pull-left,
|
||||
.fal.fa-pull-left,
|
||||
.fab.fa-pull-left {
|
||||
margin-right: .3em;
|
||||
}
|
||||
|
||||
.fa.fa-pull-right,
|
||||
.fas.fa-pull-right,
|
||||
.far.fa-pull-right,
|
||||
.fal.fa-pull-right,
|
||||
.fab.fa-pull-right {
|
||||
margin-left: .3em;
|
||||
}
|
||||
|
||||
.fa-spin {
|
||||
-webkit-animation: fa-spin 2s infinite linear;
|
||||
animation: fa-spin 2s infinite linear;
|
||||
}
|
||||
|
||||
.fa-pulse {
|
||||
-webkit-animation: fa-spin 1s infinite steps(8);
|
||||
animation: fa-spin 1s infinite steps(8);
|
||||
}
|
||||
|
||||
@-webkit-keyframes fa-spin {
|
||||
0% {
|
||||
-webkit-transform: rotate(0deg);
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
|
||||
100% {
|
||||
-webkit-transform: rotate(360deg);
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes fa-spin {
|
||||
0% {
|
||||
-webkit-transform: rotate(0deg);
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
|
||||
100% {
|
||||
-webkit-transform: rotate(360deg);
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
.fa-rotate-90 {
|
||||
-ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";
|
||||
-webkit-transform: rotate(90deg);
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
|
||||
.fa-rotate-180 {
|
||||
-ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=2)";
|
||||
-webkit-transform: rotate(180deg);
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
|
||||
.fa-rotate-270 {
|
||||
-ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";
|
||||
-webkit-transform: rotate(270deg);
|
||||
transform: rotate(270deg);
|
||||
}
|
||||
|
||||
.fa-flip-horizontal {
|
||||
-ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)";
|
||||
-webkit-transform: scale(-1, 1);
|
||||
transform: scale(-1, 1);
|
||||
}
|
||||
|
||||
.fa-flip-vertical {
|
||||
-ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)";
|
||||
-webkit-transform: scale(1, -1);
|
||||
transform: scale(1, -1);
|
||||
}
|
||||
|
||||
.fa-flip-both,
|
||||
.fa-flip-horizontal.fa-flip-vertical {
|
||||
-ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)";
|
||||
-webkit-transform: scale(-1, -1);
|
||||
transform: scale(-1, -1);
|
||||
}
|
||||
|
||||
:root .fa-rotate-90,
|
||||
:root .fa-rotate-180,
|
||||
:root .fa-rotate-270,
|
||||
:root .fa-flip-horizontal,
|
||||
:root .fa-flip-vertical,
|
||||
:root .fa-flip-both {
|
||||
-webkit-filter: none;
|
||||
filter: none;
|
||||
}
|
||||
|
||||
.fa-stack {
|
||||
display: inline-block;
|
||||
height: 2em;
|
||||
position: relative;
|
||||
width: 2.5em;
|
||||
}
|
||||
|
||||
.fa-stack-1x,
|
||||
.fa-stack-2x {
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
margin: auto;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.svg-inline--fa.fa-stack-1x {
|
||||
height: 1em;
|
||||
width: 1.25em;
|
||||
}
|
||||
|
||||
.svg-inline--fa.fa-stack-2x {
|
||||
height: 2em;
|
||||
width: 2.5em;
|
||||
}
|
||||
|
||||
.fa-inverse {
|
||||
var(--base-text);
|
||||
}
|
||||
|
||||
.sr-only {
|
||||
border: 0;
|
||||
clip: rect(0, 0, 0, 0);
|
||||
height: 1px;
|
||||
margin: -1px;
|
||||
overflow: hidden;
|
||||
padding: 0;
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
}
|
||||
|
||||
.sr-only-focusable:active,
|
||||
.sr-only-focusable:focus {
|
||||
clip: auto;
|
||||
height: auto;
|
||||
margin: 0;
|
||||
overflow: visible;
|
||||
position: static;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.svg-inline--fa .fa-primary {
|
||||
fill: var(--fa-primary-color, currentColor);
|
||||
opacity: 1;
|
||||
opacity: var(--fa-primary-opacity, 1);
|
||||
}
|
||||
|
||||
.svg-inline--fa .fa-secondary {
|
||||
fill: var(--fa-secondary-color, currentColor);
|
||||
opacity: 0.4;
|
||||
opacity: var(--fa-secondary-opacity, 0.4);
|
||||
}
|
||||
|
||||
.svg-inline--fa.fa-swap-opacity .fa-primary {
|
||||
opacity: 0.4;
|
||||
opacity: var(--fa-secondary-opacity, 0.4);
|
||||
}
|
||||
|
||||
.svg-inline--fa.fa-swap-opacity .fa-secondary {
|
||||
opacity: 1;
|
||||
opacity: var(--fa-primary-opacity, 1);
|
||||
}
|
||||
|
||||
.svg-inline--fa mask .fa-primary,
|
||||
.svg-inline--fa mask .fa-secondary {
|
||||
fill: black;
|
||||
}
|
||||
|
||||
.fad.fa-inverse {
|
||||
var(--base-text);
|
||||
}
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Before Width: | Height: | Size: 713 KiB |
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Before Width: | Height: | Size: 2.5 MiB |
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Before Width: | Height: | Size: 2.3 MiB |
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Before Width: | Height: | Size: 2.1 MiB |
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Before Width: | Height: | Size: 1.7 MiB |
Binary file not shown.
Binary file not shown.
Binary file not shown.
7946
app/frontend/static/assets/vendors/fontawesome6/css/all.css
vendored
Normal file
7946
app/frontend/static/assets/vendors/fontawesome6/css/all.css
vendored
Normal file
File diff suppressed because it is too large
Load Diff
6
app/frontend/static/assets/vendors/fontawesome6/css/all.min.css
vendored
Normal file
6
app/frontend/static/assets/vendors/fontawesome6/css/all.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
1516
app/frontend/static/assets/vendors/fontawesome6/css/brands.css
vendored
Normal file
1516
app/frontend/static/assets/vendors/fontawesome6/css/brands.css
vendored
Normal file
File diff suppressed because it is too large
Load Diff
6
app/frontend/static/assets/vendors/fontawesome6/css/brands.min.css
vendored
Normal file
6
app/frontend/static/assets/vendors/fontawesome6/css/brands.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
6369
app/frontend/static/assets/vendors/fontawesome6/css/fontawesome.css
vendored
Normal file
6369
app/frontend/static/assets/vendors/fontawesome6/css/fontawesome.css
vendored
Normal file
File diff suppressed because it is too large
Load Diff
6
app/frontend/static/assets/vendors/fontawesome6/css/fontawesome.min.css
vendored
Normal file
6
app/frontend/static/assets/vendors/fontawesome6/css/fontawesome.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
19
app/frontend/static/assets/vendors/fontawesome6/css/regular.css
vendored
Normal file
19
app/frontend/static/assets/vendors/fontawesome6/css/regular.css
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
/*!
|
||||
* Font Awesome Free 6.2.1 by @fontawesome - https://fontawesome.com
|
||||
* License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
|
||||
* Copyright 2022 Fonticons, Inc.
|
||||
*/
|
||||
:root, :host {
|
||||
--fa-style-family-classic: 'Font Awesome 6 Free';
|
||||
--fa-font-regular: normal 400 1em/1 'Font Awesome 6 Free'; }
|
||||
|
||||
@font-face {
|
||||
font-family: 'Font Awesome 6 Free';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: block;
|
||||
src: url("../webfonts/fa-regular-400.woff2") format("woff2"), url("../webfonts/fa-regular-400.ttf") format("truetype"); }
|
||||
|
||||
.far,
|
||||
.fa-regular {
|
||||
font-weight: 400; }
|
6
app/frontend/static/assets/vendors/fontawesome6/css/regular.min.css
vendored
Normal file
6
app/frontend/static/assets/vendors/fontawesome6/css/regular.min.css
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
/*!
|
||||
* Font Awesome Free 6.2.1 by @fontawesome - https://fontawesome.com
|
||||
* License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
|
||||
* Copyright 2022 Fonticons, Inc.
|
||||
*/
|
||||
:host,:root{--fa-style-family-classic:"Font Awesome 6 Free";--fa-font-regular:normal 400 1em/1 "Font Awesome 6 Free"}@font-face{font-family:"Font Awesome 6 Free";font-style:normal;font-weight:400;font-display:block;src:url(../webfonts/fa-regular-400.woff2) format("woff2"),url(../webfonts/fa-regular-400.ttf) format("truetype")}.fa-regular,.far{font-weight:400}
|
19
app/frontend/static/assets/vendors/fontawesome6/css/solid.css
vendored
Normal file
19
app/frontend/static/assets/vendors/fontawesome6/css/solid.css
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
/*!
|
||||
* Font Awesome Free 6.2.1 by @fontawesome - https://fontawesome.com
|
||||
* License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
|
||||
* Copyright 2022 Fonticons, Inc.
|
||||
*/
|
||||
:root, :host {
|
||||
--fa-style-family-classic: 'Font Awesome 6 Free';
|
||||
--fa-font-solid: normal 900 1em/1 'Font Awesome 6 Free'; }
|
||||
|
||||
@font-face {
|
||||
font-family: 'Font Awesome 6 Free';
|
||||
font-style: normal;
|
||||
font-weight: 900;
|
||||
font-display: block;
|
||||
src: url("../webfonts/fa-solid-900.woff2") format("woff2"), url("../webfonts/fa-solid-900.ttf") format("truetype"); }
|
||||
|
||||
.fas,
|
||||
.fa-solid {
|
||||
font-weight: 900; }
|
6
app/frontend/static/assets/vendors/fontawesome6/css/solid.min.css
vendored
Normal file
6
app/frontend/static/assets/vendors/fontawesome6/css/solid.min.css
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
/*!
|
||||
* Font Awesome Free 6.2.1 by @fontawesome - https://fontawesome.com
|
||||
* License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
|
||||
* Copyright 2022 Fonticons, Inc.
|
||||
*/
|
||||
:host,:root{--fa-style-family-classic:"Font Awesome 6 Free";--fa-font-solid:normal 900 1em/1 "Font Awesome 6 Free"}@font-face{font-family:"Font Awesome 6 Free";font-style:normal;font-weight:900;font-display:block;src:url(../webfonts/fa-solid-900.woff2) format("woff2"),url(../webfonts/fa-solid-900.ttf) format("truetype")}.fa-solid,.fas{font-weight:900}
|
635
app/frontend/static/assets/vendors/fontawesome6/css/svg-with-js.css
vendored
Normal file
635
app/frontend/static/assets/vendors/fontawesome6/css/svg-with-js.css
vendored
Normal file
@ -0,0 +1,635 @@
|
||||
/*!
|
||||
* Font Awesome Free 6.2.1 by @fontawesome - https://fontawesome.com
|
||||
* License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
|
||||
* Copyright 2022 Fonticons, Inc.
|
||||
*/
|
||||
:root, :host {
|
||||
--fa-font-solid: normal 900 1em/1 'Font Awesome 6 Solid';
|
||||
--fa-font-regular: normal 400 1em/1 'Font Awesome 6 Regular';
|
||||
--fa-font-light: normal 300 1em/1 'Font Awesome 6 Light';
|
||||
--fa-font-thin: normal 100 1em/1 'Font Awesome 6 Thin';
|
||||
--fa-font-duotone: normal 900 1em/1 'Font Awesome 6 Duotone';
|
||||
--fa-font-sharp-solid: normal 900 1em/1 'Font Awesome 6 Sharp';
|
||||
--fa-font-brands: normal 400 1em/1 'Font Awesome 6 Brands'; }
|
||||
|
||||
svg:not(:root).svg-inline--fa, svg:not(:host).svg-inline--fa {
|
||||
overflow: visible;
|
||||
box-sizing: content-box; }
|
||||
|
||||
.svg-inline--fa {
|
||||
display: var(--fa-display, inline-block);
|
||||
height: 1em;
|
||||
overflow: visible;
|
||||
vertical-align: -.125em; }
|
||||
.svg-inline--fa.fa-2xs {
|
||||
vertical-align: 0.1em; }
|
||||
.svg-inline--fa.fa-xs {
|
||||
vertical-align: 0em; }
|
||||
.svg-inline--fa.fa-sm {
|
||||
vertical-align: -0.07143em; }
|
||||
.svg-inline--fa.fa-lg {
|
||||
vertical-align: -0.2em; }
|
||||
.svg-inline--fa.fa-xl {
|
||||
vertical-align: -0.25em; }
|
||||
.svg-inline--fa.fa-2xl {
|
||||
vertical-align: -0.3125em; }
|
||||
.svg-inline--fa.fa-pull-left {
|
||||
margin-right: var(--fa-pull-margin, 0.3em);
|
||||
width: auto; }
|
||||
.svg-inline--fa.fa-pull-right {
|
||||
margin-left: var(--fa-pull-margin, 0.3em);
|
||||
width: auto; }
|
||||
.svg-inline--fa.fa-li {
|
||||
width: var(--fa-li-width, 2em);
|
||||
top: 0.25em; }
|
||||
.svg-inline--fa.fa-fw {
|
||||
width: var(--fa-fw-width, 1.25em); }
|
||||
|
||||
.fa-layers svg.svg-inline--fa {
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
margin: auto;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0; }
|
||||
|
||||
.fa-layers-text, .fa-layers-counter {
|
||||
display: inline-block;
|
||||
position: absolute;
|
||||
text-align: center; }
|
||||
|
||||
.fa-layers {
|
||||
display: inline-block;
|
||||
height: 1em;
|
||||
position: relative;
|
||||
text-align: center;
|
||||
vertical-align: -.125em;
|
||||
width: 1em; }
|
||||
.fa-layers svg.svg-inline--fa {
|
||||
-webkit-transform-origin: center center;
|
||||
transform-origin: center center; }
|
||||
|
||||
.fa-layers-text {
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
-webkit-transform: translate(-50%, -50%);
|
||||
transform: translate(-50%, -50%);
|
||||
-webkit-transform-origin: center center;
|
||||
transform-origin: center center; }
|
||||
|
||||
.fa-layers-counter {
|
||||
background-color: var(--fa-counter-background-color, #ff253a);
|
||||
border-radius: var(--fa-counter-border-radius, 1em);
|
||||
box-sizing: border-box;
|
||||
color: var(--fa-inverse, #fff);
|
||||
line-height: var(--fa-counter-line-height, 1);
|
||||
max-width: var(--fa-counter-max-width, 5em);
|
||||
min-width: var(--fa-counter-min-width, 1.5em);
|
||||
overflow: hidden;
|
||||
padding: var(--fa-counter-padding, 0.25em 0.5em);
|
||||
right: var(--fa-right, 0);
|
||||
text-overflow: ellipsis;
|
||||
top: var(--fa-top, 0);
|
||||
-webkit-transform: scale(var(--fa-counter-scale, 0.25));
|
||||
transform: scale(var(--fa-counter-scale, 0.25));
|
||||
-webkit-transform-origin: top right;
|
||||
transform-origin: top right; }
|
||||
|
||||
.fa-layers-bottom-right {
|
||||
bottom: var(--fa-bottom, 0);
|
||||
right: var(--fa-right, 0);
|
||||
top: auto;
|
||||
-webkit-transform: scale(var(--fa-layers-scale, 0.25));
|
||||
transform: scale(var(--fa-layers-scale, 0.25));
|
||||
-webkit-transform-origin: bottom right;
|
||||
transform-origin: bottom right; }
|
||||
|
||||
.fa-layers-bottom-left {
|
||||
bottom: var(--fa-bottom, 0);
|
||||
left: var(--fa-left, 0);
|
||||
right: auto;
|
||||
top: auto;
|
||||
-webkit-transform: scale(var(--fa-layers-scale, 0.25));
|
||||
transform: scale(var(--fa-layers-scale, 0.25));
|
||||
-webkit-transform-origin: bottom left;
|
||||
transform-origin: bottom left; }
|
||||
|
||||
.fa-layers-top-right {
|
||||
top: var(--fa-top, 0);
|
||||
right: var(--fa-right, 0);
|
||||
-webkit-transform: scale(var(--fa-layers-scale, 0.25));
|
||||
transform: scale(var(--fa-layers-scale, 0.25));
|
||||
-webkit-transform-origin: top right;
|
||||
transform-origin: top right; }
|
||||
|
||||
.fa-layers-top-left {
|
||||
left: var(--fa-left, 0);
|
||||
right: auto;
|
||||
top: var(--fa-top, 0);
|
||||
-webkit-transform: scale(var(--fa-layers-scale, 0.25));
|
||||
transform: scale(var(--fa-layers-scale, 0.25));
|
||||
-webkit-transform-origin: top left;
|
||||
transform-origin: top left; }
|
||||
|
||||
.fa-1x {
|
||||
font-size: 1em; }
|
||||
|
||||
.fa-2x {
|
||||
font-size: 2em; }
|
||||
|
||||
.fa-3x {
|
||||
font-size: 3em; }
|
||||
|
||||
.fa-4x {
|
||||
font-size: 4em; }
|
||||
|
||||
.fa-5x {
|
||||
font-size: 5em; }
|
||||
|
||||
.fa-6x {
|
||||
font-size: 6em; }
|
||||
|
||||
.fa-7x {
|
||||
font-size: 7em; }
|
||||
|
||||
.fa-8x {
|
||||
font-size: 8em; }
|
||||
|
||||
.fa-9x {
|
||||
font-size: 9em; }
|
||||
|
||||
.fa-10x {
|
||||
font-size: 10em; }
|
||||
|
||||
.fa-2xs {
|
||||
font-size: 0.625em;
|
||||
line-height: 0.1em;
|
||||
vertical-align: 0.225em; }
|
||||
|
||||
.fa-xs {
|
||||
font-size: 0.75em;
|
||||
line-height: 0.08333em;
|
||||
vertical-align: 0.125em; }
|
||||
|
||||
.fa-sm {
|
||||
font-size: 0.875em;
|
||||
line-height: 0.07143em;
|
||||
vertical-align: 0.05357em; }
|
||||
|
||||
.fa-lg {
|
||||
font-size: 1.25em;
|
||||
line-height: 0.05em;
|
||||
vertical-align: -0.075em; }
|
||||
|
||||
.fa-xl {
|
||||
font-size: 1.5em;
|
||||
line-height: 0.04167em;
|
||||
vertical-align: -0.125em; }
|
||||
|
||||
.fa-2xl {
|
||||
font-size: 2em;
|
||||
line-height: 0.03125em;
|
||||
vertical-align: -0.1875em; }
|
||||
|
||||
.fa-fw {
|
||||
text-align: center;
|
||||
width: 1.25em; }
|
||||
|
||||
.fa-ul {
|
||||
list-style-type: none;
|
||||
margin-left: var(--fa-li-margin, 2.5em);
|
||||
padding-left: 0; }
|
||||
.fa-ul > li {
|
||||
position: relative; }
|
||||
|
||||
.fa-li {
|
||||
left: calc(var(--fa-li-width, 2em) * -1);
|
||||
position: absolute;
|
||||
text-align: center;
|
||||
width: var(--fa-li-width, 2em);
|
||||
line-height: inherit; }
|
||||
|
||||
.fa-border {
|
||||
border-color: var(--fa-border-color, #eee);
|
||||
border-radius: var(--fa-border-radius, 0.1em);
|
||||
border-style: var(--fa-border-style, solid);
|
||||
border-width: var(--fa-border-width, 0.08em);
|
||||
padding: var(--fa-border-padding, 0.2em 0.25em 0.15em); }
|
||||
|
||||
.fa-pull-left {
|
||||
float: left;
|
||||
margin-right: var(--fa-pull-margin, 0.3em); }
|
||||
|
||||
.fa-pull-right {
|
||||
float: right;
|
||||
margin-left: var(--fa-pull-margin, 0.3em); }
|
||||
|
||||
.fa-beat {
|
||||
-webkit-animation-name: fa-beat;
|
||||
animation-name: fa-beat;
|
||||
-webkit-animation-delay: var(--fa-animation-delay, 0s);
|
||||
animation-delay: var(--fa-animation-delay, 0s);
|
||||
-webkit-animation-direction: var(--fa-animation-direction, normal);
|
||||
animation-direction: var(--fa-animation-direction, normal);
|
||||
-webkit-animation-duration: var(--fa-animation-duration, 1s);
|
||||
animation-duration: var(--fa-animation-duration, 1s);
|
||||
-webkit-animation-iteration-count: var(--fa-animation-iteration-count, infinite);
|
||||
animation-iteration-count: var(--fa-animation-iteration-count, infinite);
|
||||
-webkit-animation-timing-function: var(--fa-animation-timing, ease-in-out);
|
||||
animation-timing-function: var(--fa-animation-timing, ease-in-out); }
|
||||
|
||||
.fa-bounce {
|
||||
-webkit-animation-name: fa-bounce;
|
||||
animation-name: fa-bounce;
|
||||
-webkit-animation-delay: var(--fa-animation-delay, 0s);
|
||||
animation-delay: var(--fa-animation-delay, 0s);
|
||||
-webkit-animation-direction: var(--fa-animation-direction, normal);
|
||||
animation-direction: var(--fa-animation-direction, normal);
|
||||
-webkit-animation-duration: var(--fa-animation-duration, 1s);
|
||||
animation-duration: var(--fa-animation-duration, 1s);
|
||||
-webkit-animation-iteration-count: var(--fa-animation-iteration-count, infinite);
|
||||
animation-iteration-count: var(--fa-animation-iteration-count, infinite);
|
||||
-webkit-animation-timing-function: var(--fa-animation-timing, cubic-bezier(0.28, 0.84, 0.42, 1));
|
||||
animation-timing-function: var(--fa-animation-timing, cubic-bezier(0.28, 0.84, 0.42, 1)); }
|
||||
|
||||
.fa-fade {
|
||||
-webkit-animation-name: fa-fade;
|
||||
animation-name: fa-fade;
|
||||
-webkit-animation-delay: var(--fa-animation-delay, 0s);
|
||||
animation-delay: var(--fa-animation-delay, 0s);
|
||||
-webkit-animation-direction: var(--fa-animation-direction, normal);
|
||||
animation-direction: var(--fa-animation-direction, normal);
|
||||
-webkit-animation-duration: var(--fa-animation-duration, 1s);
|
||||
animation-duration: var(--fa-animation-duration, 1s);
|
||||
-webkit-animation-iteration-count: var(--fa-animation-iteration-count, infinite);
|
||||
animation-iteration-count: var(--fa-animation-iteration-count, infinite);
|
||||
-webkit-animation-timing-function: var(--fa-animation-timing, cubic-bezier(0.4, 0, 0.6, 1));
|
||||
animation-timing-function: var(--fa-animation-timing, cubic-bezier(0.4, 0, 0.6, 1)); }
|
||||
|
||||
.fa-beat-fade {
|
||||
-webkit-animation-name: fa-beat-fade;
|
||||
animation-name: fa-beat-fade;
|
||||
-webkit-animation-delay: var(--fa-animation-delay, 0s);
|
||||
animation-delay: var(--fa-animation-delay, 0s);
|
||||
-webkit-animation-direction: var(--fa-animation-direction, normal);
|
||||
animation-direction: var(--fa-animation-direction, normal);
|
||||
-webkit-animation-duration: var(--fa-animation-duration, 1s);
|
||||
animation-duration: var(--fa-animation-duration, 1s);
|
||||
-webkit-animation-iteration-count: var(--fa-animation-iteration-count, infinite);
|
||||
animation-iteration-count: var(--fa-animation-iteration-count, infinite);
|
||||
-webkit-animation-timing-function: var(--fa-animation-timing, cubic-bezier(0.4, 0, 0.6, 1));
|
||||
animation-timing-function: var(--fa-animation-timing, cubic-bezier(0.4, 0, 0.6, 1)); }
|
||||
|
||||
.fa-flip {
|
||||
-webkit-animation-name: fa-flip;
|
||||
animation-name: fa-flip;
|
||||
-webkit-animation-delay: var(--fa-animation-delay, 0s);
|
||||
animation-delay: var(--fa-animation-delay, 0s);
|
||||
-webkit-animation-direction: var(--fa-animation-direction, normal);
|
||||
animation-direction: var(--fa-animation-direction, normal);
|
||||
-webkit-animation-duration: var(--fa-animation-duration, 1s);
|
||||
animation-duration: var(--fa-animation-duration, 1s);
|
||||
-webkit-animation-iteration-count: var(--fa-animation-iteration-count, infinite);
|
||||
animation-iteration-count: var(--fa-animation-iteration-count, infinite);
|
||||
-webkit-animation-timing-function: var(--fa-animation-timing, ease-in-out);
|
||||
animation-timing-function: var(--fa-animation-timing, ease-in-out); }
|
||||
|
||||
.fa-shake {
|
||||
-webkit-animation-name: fa-shake;
|
||||
animation-name: fa-shake;
|
||||
-webkit-animation-delay: var(--fa-animation-delay, 0s);
|
||||
animation-delay: var(--fa-animation-delay, 0s);
|
||||
-webkit-animation-direction: var(--fa-animation-direction, normal);
|
||||
animation-direction: var(--fa-animation-direction, normal);
|
||||
-webkit-animation-duration: var(--fa-animation-duration, 1s);
|
||||
animation-duration: var(--fa-animation-duration, 1s);
|
||||
-webkit-animation-iteration-count: var(--fa-animation-iteration-count, infinite);
|
||||
animation-iteration-count: var(--fa-animation-iteration-count, infinite);
|
||||
-webkit-animation-timing-function: var(--fa-animation-timing, linear);
|
||||
animation-timing-function: var(--fa-animation-timing, linear); }
|
||||
|
||||
.fa-spin {
|
||||
-webkit-animation-name: fa-spin;
|
||||
animation-name: fa-spin;
|
||||
-webkit-animation-delay: var(--fa-animation-delay, 0s);
|
||||
animation-delay: var(--fa-animation-delay, 0s);
|
||||
-webkit-animation-direction: var(--fa-animation-direction, normal);
|
||||
animation-direction: var(--fa-animation-direction, normal);
|
||||
-webkit-animation-duration: var(--fa-animation-duration, 2s);
|
||||
animation-duration: var(--fa-animation-duration, 2s);
|
||||
-webkit-animation-iteration-count: var(--fa-animation-iteration-count, infinite);
|
||||
animation-iteration-count: var(--fa-animation-iteration-count, infinite);
|
||||
-webkit-animation-timing-function: var(--fa-animation-timing, linear);
|
||||
animation-timing-function: var(--fa-animation-timing, linear); }
|
||||
|
||||
.fa-spin-reverse {
|
||||
--fa-animation-direction: reverse; }
|
||||
|
||||
.fa-pulse,
|
||||
.fa-spin-pulse {
|
||||
-webkit-animation-name: fa-spin;
|
||||
animation-name: fa-spin;
|
||||
-webkit-animation-direction: var(--fa-animation-direction, normal);
|
||||
animation-direction: var(--fa-animation-direction, normal);
|
||||
-webkit-animation-duration: var(--fa-animation-duration, 1s);
|
||||
animation-duration: var(--fa-animation-duration, 1s);
|
||||
-webkit-animation-iteration-count: var(--fa-animation-iteration-count, infinite);
|
||||
animation-iteration-count: var(--fa-animation-iteration-count, infinite);
|
||||
-webkit-animation-timing-function: var(--fa-animation-timing, steps(8));
|
||||
animation-timing-function: var(--fa-animation-timing, steps(8)); }
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.fa-beat,
|
||||
.fa-bounce,
|
||||
.fa-fade,
|
||||
.fa-beat-fade,
|
||||
.fa-flip,
|
||||
.fa-pulse,
|
||||
.fa-shake,
|
||||
.fa-spin,
|
||||
.fa-spin-pulse {
|
||||
-webkit-animation-delay: -1ms;
|
||||
animation-delay: -1ms;
|
||||
-webkit-animation-duration: 1ms;
|
||||
animation-duration: 1ms;
|
||||
-webkit-animation-iteration-count: 1;
|
||||
animation-iteration-count: 1;
|
||||
transition-delay: 0s;
|
||||
transition-duration: 0s; } }
|
||||
|
||||
@-webkit-keyframes fa-beat {
|
||||
0%, 90% {
|
||||
-webkit-transform: scale(1);
|
||||
transform: scale(1); }
|
||||
45% {
|
||||
-webkit-transform: scale(var(--fa-beat-scale, 1.25));
|
||||
transform: scale(var(--fa-beat-scale, 1.25)); } }
|
||||
|
||||
@keyframes fa-beat {
|
||||
0%, 90% {
|
||||
-webkit-transform: scale(1);
|
||||
transform: scale(1); }
|
||||
45% {
|
||||
-webkit-transform: scale(var(--fa-beat-scale, 1.25));
|
||||
transform: scale(var(--fa-beat-scale, 1.25)); } }
|
||||
|
||||
@-webkit-keyframes fa-bounce {
|
||||
0% {
|
||||
-webkit-transform: scale(1, 1) translateY(0);
|
||||
transform: scale(1, 1) translateY(0); }
|
||||
10% {
|
||||
-webkit-transform: scale(var(--fa-bounce-start-scale-x, 1.1), var(--fa-bounce-start-scale-y, 0.9)) translateY(0);
|
||||
transform: scale(var(--fa-bounce-start-scale-x, 1.1), var(--fa-bounce-start-scale-y, 0.9)) translateY(0); }
|
||||
30% {
|
||||
-webkit-transform: scale(var(--fa-bounce-jump-scale-x, 0.9), var(--fa-bounce-jump-scale-y, 1.1)) translateY(var(--fa-bounce-height, -0.5em));
|
||||
transform: scale(var(--fa-bounce-jump-scale-x, 0.9), var(--fa-bounce-jump-scale-y, 1.1)) translateY(var(--fa-bounce-height, -0.5em)); }
|
||||
50% {
|
||||
-webkit-transform: scale(var(--fa-bounce-land-scale-x, 1.05), var(--fa-bounce-land-scale-y, 0.95)) translateY(0);
|
||||
transform: scale(var(--fa-bounce-land-scale-x, 1.05), var(--fa-bounce-land-scale-y, 0.95)) translateY(0); }
|
||||
57% {
|
||||
-webkit-transform: scale(1, 1) translateY(var(--fa-bounce-rebound, -0.125em));
|
||||
transform: scale(1, 1) translateY(var(--fa-bounce-rebound, -0.125em)); }
|
||||
64% {
|
||||
-webkit-transform: scale(1, 1) translateY(0);
|
||||
transform: scale(1, 1) translateY(0); }
|
||||
100% {
|
||||
-webkit-transform: scale(1, 1) translateY(0);
|
||||
transform: scale(1, 1) translateY(0); } }
|
||||
|
||||
@keyframes fa-bounce {
|
||||
0% {
|
||||
-webkit-transform: scale(1, 1) translateY(0);
|
||||
transform: scale(1, 1) translateY(0); }
|
||||
10% {
|
||||
-webkit-transform: scale(var(--fa-bounce-start-scale-x, 1.1), var(--fa-bounce-start-scale-y, 0.9)) translateY(0);
|
||||
transform: scale(var(--fa-bounce-start-scale-x, 1.1), var(--fa-bounce-start-scale-y, 0.9)) translateY(0); }
|
||||
30% {
|
||||
-webkit-transform: scale(var(--fa-bounce-jump-scale-x, 0.9), var(--fa-bounce-jump-scale-y, 1.1)) translateY(var(--fa-bounce-height, -0.5em));
|
||||
transform: scale(var(--fa-bounce-jump-scale-x, 0.9), var(--fa-bounce-jump-scale-y, 1.1)) translateY(var(--fa-bounce-height, -0.5em)); }
|
||||
50% {
|
||||
-webkit-transform: scale(var(--fa-bounce-land-scale-x, 1.05), var(--fa-bounce-land-scale-y, 0.95)) translateY(0);
|
||||
transform: scale(var(--fa-bounce-land-scale-x, 1.05), var(--fa-bounce-land-scale-y, 0.95)) translateY(0); }
|
||||
57% {
|
||||
-webkit-transform: scale(1, 1) translateY(var(--fa-bounce-rebound, -0.125em));
|
||||
transform: scale(1, 1) translateY(var(--fa-bounce-rebound, -0.125em)); }
|
||||
64% {
|
||||
-webkit-transform: scale(1, 1) translateY(0);
|
||||
transform: scale(1, 1) translateY(0); }
|
||||
100% {
|
||||
-webkit-transform: scale(1, 1) translateY(0);
|
||||
transform: scale(1, 1) translateY(0); } }
|
||||
|
||||
@-webkit-keyframes fa-fade {
|
||||
50% {
|
||||
opacity: var(--fa-fade-opacity, 0.4); } }
|
||||
|
||||
@keyframes fa-fade {
|
||||
50% {
|
||||
opacity: var(--fa-fade-opacity, 0.4); } }
|
||||
|
||||
@-webkit-keyframes fa-beat-fade {
|
||||
0%, 100% {
|
||||
opacity: var(--fa-beat-fade-opacity, 0.4);
|
||||
-webkit-transform: scale(1);
|
||||
transform: scale(1); }
|
||||
50% {
|
||||
opacity: 1;
|
||||
-webkit-transform: scale(var(--fa-beat-fade-scale, 1.125));
|
||||
transform: scale(var(--fa-beat-fade-scale, 1.125)); } }
|
||||
|
||||
@keyframes fa-beat-fade {
|
||||
0%, 100% {
|
||||
opacity: var(--fa-beat-fade-opacity, 0.4);
|
||||
-webkit-transform: scale(1);
|
||||
transform: scale(1); }
|
||||
50% {
|
||||
opacity: 1;
|
||||
-webkit-transform: scale(var(--fa-beat-fade-scale, 1.125));
|
||||
transform: scale(var(--fa-beat-fade-scale, 1.125)); } }
|
||||
|
||||
@-webkit-keyframes fa-flip {
|
||||
50% {
|
||||
-webkit-transform: rotate3d(var(--fa-flip-x, 0), var(--fa-flip-y, 1), var(--fa-flip-z, 0), var(--fa-flip-angle, -180deg));
|
||||
transform: rotate3d(var(--fa-flip-x, 0), var(--fa-flip-y, 1), var(--fa-flip-z, 0), var(--fa-flip-angle, -180deg)); } }
|
||||
|
||||
@keyframes fa-flip {
|
||||
50% {
|
||||
-webkit-transform: rotate3d(var(--fa-flip-x, 0), var(--fa-flip-y, 1), var(--fa-flip-z, 0), var(--fa-flip-angle, -180deg));
|
||||
transform: rotate3d(var(--fa-flip-x, 0), var(--fa-flip-y, 1), var(--fa-flip-z, 0), var(--fa-flip-angle, -180deg)); } }
|
||||
|
||||
@-webkit-keyframes fa-shake {
|
||||
0% {
|
||||
-webkit-transform: rotate(-15deg);
|
||||
transform: rotate(-15deg); }
|
||||
4% {
|
||||
-webkit-transform: rotate(15deg);
|
||||
transform: rotate(15deg); }
|
||||
8%, 24% {
|
||||
-webkit-transform: rotate(-18deg);
|
||||
transform: rotate(-18deg); }
|
||||
12%, 28% {
|
||||
-webkit-transform: rotate(18deg);
|
||||
transform: rotate(18deg); }
|
||||
16% {
|
||||
-webkit-transform: rotate(-22deg);
|
||||
transform: rotate(-22deg); }
|
||||
20% {
|
||||
-webkit-transform: rotate(22deg);
|
||||
transform: rotate(22deg); }
|
||||
32% {
|
||||
-webkit-transform: rotate(-12deg);
|
||||
transform: rotate(-12deg); }
|
||||
36% {
|
||||
-webkit-transform: rotate(12deg);
|
||||
transform: rotate(12deg); }
|
||||
40%, 100% {
|
||||
-webkit-transform: rotate(0deg);
|
||||
transform: rotate(0deg); } }
|
||||
|
||||
@keyframes fa-shake {
|
||||
0% {
|
||||
-webkit-transform: rotate(-15deg);
|
||||
transform: rotate(-15deg); }
|
||||
4% {
|
||||
-webkit-transform: rotate(15deg);
|
||||
transform: rotate(15deg); }
|
||||
8%, 24% {
|
||||
-webkit-transform: rotate(-18deg);
|
||||
transform: rotate(-18deg); }
|
||||
12%, 28% {
|
||||
-webkit-transform: rotate(18deg);
|
||||
transform: rotate(18deg); }
|
||||
16% {
|
||||
-webkit-transform: rotate(-22deg);
|
||||
transform: rotate(-22deg); }
|
||||
20% {
|
||||
-webkit-transform: rotate(22deg);
|
||||
transform: rotate(22deg); }
|
||||
32% {
|
||||
-webkit-transform: rotate(-12deg);
|
||||
transform: rotate(-12deg); }
|
||||
36% {
|
||||
-webkit-transform: rotate(12deg);
|
||||
transform: rotate(12deg); }
|
||||
40%, 100% {
|
||||
-webkit-transform: rotate(0deg);
|
||||
transform: rotate(0deg); } }
|
||||
|
||||
@-webkit-keyframes fa-spin {
|
||||
0% {
|
||||
-webkit-transform: rotate(0deg);
|
||||
transform: rotate(0deg); }
|
||||
100% {
|
||||
-webkit-transform: rotate(360deg);
|
||||
transform: rotate(360deg); } }
|
||||
|
||||
@keyframes fa-spin {
|
||||
0% {
|
||||
-webkit-transform: rotate(0deg);
|
||||
transform: rotate(0deg); }
|
||||
100% {
|
||||
-webkit-transform: rotate(360deg);
|
||||
transform: rotate(360deg); } }
|
||||
|
||||
.fa-rotate-90 {
|
||||
-webkit-transform: rotate(90deg);
|
||||
transform: rotate(90deg); }
|
||||
|
||||
.fa-rotate-180 {
|
||||
-webkit-transform: rotate(180deg);
|
||||
transform: rotate(180deg); }
|
||||
|
||||
.fa-rotate-270 {
|
||||
-webkit-transform: rotate(270deg);
|
||||
transform: rotate(270deg); }
|
||||
|
||||
.fa-flip-horizontal {
|
||||
-webkit-transform: scale(-1, 1);
|
||||
transform: scale(-1, 1); }
|
||||
|
||||
.fa-flip-vertical {
|
||||
-webkit-transform: scale(1, -1);
|
||||
transform: scale(1, -1); }
|
||||
|
||||
.fa-flip-both,
|
||||
.fa-flip-horizontal.fa-flip-vertical {
|
||||
-webkit-transform: scale(-1, -1);
|
||||
transform: scale(-1, -1); }
|
||||
|
||||
.fa-rotate-by {
|
||||
-webkit-transform: rotate(var(--fa-rotate-angle, none));
|
||||
transform: rotate(var(--fa-rotate-angle, none)); }
|
||||
|
||||
.fa-stack {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
height: 2em;
|
||||
position: relative;
|
||||
width: 2.5em; }
|
||||
|
||||
.fa-stack-1x,
|
||||
.fa-stack-2x {
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
margin: auto;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
z-index: var(--fa-stack-z-index, auto); }
|
||||
|
||||
.svg-inline--fa.fa-stack-1x {
|
||||
height: 1em;
|
||||
width: 1.25em; }
|
||||
|
||||
.svg-inline--fa.fa-stack-2x {
|
||||
height: 2em;
|
||||
width: 2.5em; }
|
||||
|
||||
.fa-inverse {
|
||||
color: var(--fa-inverse, #fff); }
|
||||
|
||||
.sr-only,
|
||||
.fa-sr-only {
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
padding: 0;
|
||||
margin: -1px;
|
||||
overflow: hidden;
|
||||
clip: rect(0, 0, 0, 0);
|
||||
white-space: nowrap;
|
||||
border-width: 0; }
|
||||
|
||||
.sr-only-focusable:not(:focus),
|
||||
.fa-sr-only-focusable:not(:focus) {
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
padding: 0;
|
||||
margin: -1px;
|
||||
overflow: hidden;
|
||||
clip: rect(0, 0, 0, 0);
|
||||
white-space: nowrap;
|
||||
border-width: 0; }
|
||||
|
||||
.svg-inline--fa .fa-primary {
|
||||
fill: var(--fa-primary-color, currentColor);
|
||||
opacity: var(--fa-primary-opacity, 1); }
|
||||
|
||||
.svg-inline--fa .fa-secondary {
|
||||
fill: var(--fa-secondary-color, currentColor);
|
||||
opacity: var(--fa-secondary-opacity, 0.4); }
|
||||
|
||||
.svg-inline--fa.fa-swap-opacity .fa-primary {
|
||||
opacity: var(--fa-secondary-opacity, 0.4); }
|
||||
|
||||
.svg-inline--fa.fa-swap-opacity .fa-secondary {
|
||||
opacity: var(--fa-primary-opacity, 1); }
|
||||
|
||||
.svg-inline--fa mask .fa-primary,
|
||||
.svg-inline--fa mask .fa-secondary {
|
||||
fill: black; }
|
||||
|
||||
.fad.fa-inverse,
|
||||
.fa-duotone.fa-inverse {
|
||||
color: var(--fa-inverse, #fff); }
|
6
app/frontend/static/assets/vendors/fontawesome6/css/svg-with-js.min.css
vendored
Normal file
6
app/frontend/static/assets/vendors/fontawesome6/css/svg-with-js.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
26
app/frontend/static/assets/vendors/fontawesome6/css/v4-font-face.css
vendored
Normal file
26
app/frontend/static/assets/vendors/fontawesome6/css/v4-font-face.css
vendored
Normal file
@ -0,0 +1,26 @@
|
||||
/*!
|
||||
* Font Awesome Free 6.2.1 by @fontawesome - https://fontawesome.com
|
||||
* License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
|
||||
* Copyright 2022 Fonticons, Inc.
|
||||
*/
|
||||
@font-face {
|
||||
font-family: 'FontAwesome';
|
||||
font-display: block;
|
||||
src: url("../webfonts/fa-solid-900.woff2") format("woff2"), url("../webfonts/fa-solid-900.ttf") format("truetype"); }
|
||||
|
||||
@font-face {
|
||||
font-family: 'FontAwesome';
|
||||
font-display: block;
|
||||
src: url("../webfonts/fa-brands-400.woff2") format("woff2"), url("../webfonts/fa-brands-400.ttf") format("truetype"); }
|
||||
|
||||
@font-face {
|
||||
font-family: 'FontAwesome';
|
||||
font-display: block;
|
||||
src: url("../webfonts/fa-regular-400.woff2") format("woff2"), url("../webfonts/fa-regular-400.ttf") format("truetype");
|
||||
unicode-range: U+F003,U+F006,U+F014,U+F016-F017,U+F01A-F01B,U+F01D,U+F022,U+F03E,U+F044,U+F046,U+F05C-F05D,U+F06E,U+F070,U+F087-F088,U+F08A,U+F094,U+F096-F097,U+F09D,U+F0A0,U+F0A2,U+F0A4-F0A7,U+F0C5,U+F0C7,U+F0E5-F0E6,U+F0EB,U+F0F6-F0F8,U+F10C,U+F114-F115,U+F118-F11A,U+F11C-F11D,U+F133,U+F147,U+F14E,U+F150-F152,U+F185-F186,U+F18E,U+F190-F192,U+F196,U+F1C1-F1C9,U+F1D9,U+F1DB,U+F1E3,U+F1EA,U+F1F7,U+F1F9,U+F20A,U+F247-F248,U+F24A,U+F24D,U+F255-F25B,U+F25D,U+F271-F274,U+F278,U+F27B,U+F28C,U+F28E,U+F29C,U+F2B5,U+F2B7,U+F2BA,U+F2BC,U+F2BE,U+F2C0-F2C1,U+F2C3,U+F2D0,U+F2D2,U+F2D4,U+F2DC; }
|
||||
|
||||
@font-face {
|
||||
font-family: 'FontAwesome';
|
||||
font-display: block;
|
||||
src: url("../webfonts/fa-v4compatibility.woff2") format("woff2"), url("../webfonts/fa-v4compatibility.ttf") format("truetype");
|
||||
unicode-range: U+F041,U+F047,U+F065-F066,U+F07D-F07E,U+F080,U+F08B,U+F08E,U+F090,U+F09A,U+F0AC,U+F0AE,U+F0B2,U+F0D0,U+F0D6,U+F0E4,U+F0EC,U+F10A-F10B,U+F123,U+F13E,U+F148-F149,U+F14C,U+F156,U+F15E,U+F160-F161,U+F163,U+F175-F178,U+F195,U+F1F8,U+F219,U+F27A; }
|
6
app/frontend/static/assets/vendors/fontawesome6/css/v4-font-face.min.css
vendored
Normal file
6
app/frontend/static/assets/vendors/fontawesome6/css/v4-font-face.min.css
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
/*!
|
||||
* Font Awesome Free 6.2.1 by @fontawesome - https://fontawesome.com
|
||||
* License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
|
||||
* Copyright 2022 Fonticons, Inc.
|
||||
*/
|
||||
@font-face{font-family:"FontAwesome";font-display:block;src:url(../webfonts/fa-solid-900.woff2) format("woff2"),url(../webfonts/fa-solid-900.ttf) format("truetype")}@font-face{font-family:"FontAwesome";font-display:block;src:url(../webfonts/fa-brands-400.woff2) format("woff2"),url(../webfonts/fa-brands-400.ttf) format("truetype")}@font-face{font-family:"FontAwesome";font-display:block;src:url(../webfonts/fa-regular-400.woff2) format("woff2"),url(../webfonts/fa-regular-400.ttf) format("truetype");unicode-range:u+f003,u+f006,u+f014,u+f016-f017,u+f01a-f01b,u+f01d,u+f022,u+f03e,u+f044,u+f046,u+f05c-f05d,u+f06e,u+f070,u+f087-f088,u+f08a,u+f094,u+f096-f097,u+f09d,u+f0a0,u+f0a2,u+f0a4-f0a7,u+f0c5,u+f0c7,u+f0e5-f0e6,u+f0eb,u+f0f6-f0f8,u+f10c,u+f114-f115,u+f118-f11a,u+f11c-f11d,u+f133,u+f147,u+f14e,u+f150-f152,u+f185-f186,u+f18e,u+f190-f192,u+f196,u+f1c1-f1c9,u+f1d9,u+f1db,u+f1e3,u+f1ea,u+f1f7,u+f1f9,u+f20a,u+f247-f248,u+f24a,u+f24d,u+f255-f25b,u+f25d,u+f271-f274,u+f278,u+f27b,u+f28c,u+f28e,u+f29c,u+f2b5,u+f2b7,u+f2ba,u+f2bc,u+f2be,u+f2c0-f2c1,u+f2c3,u+f2d0,u+f2d2,u+f2d4,u+f2dc}@font-face{font-family:"FontAwesome";font-display:block;src:url(../webfonts/fa-v4compatibility.woff2) format("woff2"),url(../webfonts/fa-v4compatibility.ttf) format("truetype");unicode-range:u+f041,u+f047,u+f065-f066,u+f07d-f07e,u+f080,u+f08b,u+f08e,u+f090,u+f09a,u+f0ac,u+f0ae,u+f0b2,u+f0d0,u+f0d6,u+f0e4,u+f0ec,u+f10a-f10b,u+f123,u+f13e,u+f148-f149,u+f14c,u+f156,u+f15e,u+f160-f161,u+f163,u+f175-f178,u+f195,u+f1f8,u+f219,u+f27a}
|
File diff suppressed because it is too large
Load Diff
6
app/frontend/static/assets/vendors/fontawesome6/css/v4-shims.min.css
vendored
Normal file
6
app/frontend/static/assets/vendors/fontawesome6/css/v4-shims.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
22
app/frontend/static/assets/vendors/fontawesome6/css/v5-font-face.css
vendored
Normal file
22
app/frontend/static/assets/vendors/fontawesome6/css/v5-font-face.css
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
/*!
|
||||
* Font Awesome Free 6.2.1 by @fontawesome - https://fontawesome.com
|
||||
* License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
|
||||
* Copyright 2022 Fonticons, Inc.
|
||||
*/
|
||||
@font-face {
|
||||
font-family: 'Font Awesome 5 Brands';
|
||||
font-display: block;
|
||||
font-weight: 400;
|
||||
src: url("../webfonts/fa-brands-400.woff2") format("woff2"), url("../webfonts/fa-brands-400.ttf") format("truetype"); }
|
||||
|
||||
@font-face {
|
||||
font-family: 'Font Awesome 5 Free';
|
||||
font-display: block;
|
||||
font-weight: 900;
|
||||
src: url("../webfonts/fa-solid-900.woff2") format("woff2"), url("../webfonts/fa-solid-900.ttf") format("truetype"); }
|
||||
|
||||
@font-face {
|
||||
font-family: 'Font Awesome 5 Free';
|
||||
font-display: block;
|
||||
font-weight: 400;
|
||||
src: url("../webfonts/fa-regular-400.woff2") format("woff2"), url("../webfonts/fa-regular-400.ttf") format("truetype"); }
|
6
app/frontend/static/assets/vendors/fontawesome6/css/v5-font-face.min.css
vendored
Normal file
6
app/frontend/static/assets/vendors/fontawesome6/css/v5-font-face.min.css
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
/*!
|
||||
* Font Awesome Free 6.2.1 by @fontawesome - https://fontawesome.com
|
||||
* License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
|
||||
* Copyright 2022 Fonticons, Inc.
|
||||
*/
|
||||
@font-face{font-family:"Font Awesome 5 Brands";font-display:block;font-weight:400;src:url(../webfonts/fa-brands-400.woff2) format("woff2"),url(../webfonts/fa-brands-400.ttf) format("truetype")}@font-face{font-family:"Font Awesome 5 Free";font-display:block;font-weight:900;src:url(../webfonts/fa-solid-900.woff2) format("woff2"),url(../webfonts/fa-solid-900.ttf) format("truetype")}@font-face{font-family:"Font Awesome 5 Free";font-display:block;font-weight:400;src:url(../webfonts/fa-regular-400.woff2) format("woff2"),url(../webfonts/fa-regular-400.ttf) format("truetype")}
|
BIN
app/frontend/static/assets/vendors/fontawesome6/webfonts/fa-brands-400.ttf
vendored
Normal file
BIN
app/frontend/static/assets/vendors/fontawesome6/webfonts/fa-brands-400.ttf
vendored
Normal file
Binary file not shown.
BIN
app/frontend/static/assets/vendors/fontawesome6/webfonts/fa-brands-400.woff2
vendored
Normal file
BIN
app/frontend/static/assets/vendors/fontawesome6/webfonts/fa-brands-400.woff2
vendored
Normal file
Binary file not shown.
BIN
app/frontend/static/assets/vendors/fontawesome6/webfonts/fa-regular-400.ttf
vendored
Normal file
BIN
app/frontend/static/assets/vendors/fontawesome6/webfonts/fa-regular-400.ttf
vendored
Normal file
Binary file not shown.
BIN
app/frontend/static/assets/vendors/fontawesome6/webfonts/fa-regular-400.woff2
vendored
Normal file
BIN
app/frontend/static/assets/vendors/fontawesome6/webfonts/fa-regular-400.woff2
vendored
Normal file
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user