mirror of
https://gitlab.com/crafty-controller/crafty-4.git
synced 2025-01-19 09:45:28 +01:00
Merge branch 'dev' into bugfix/creation-formatting
This commit is contained in:
commit
1beb86bc8f
@ -3,6 +3,7 @@ docker/
|
||||
.dockerignore
|
||||
Dockerfile
|
||||
docker-compose.yml
|
||||
docker-compose.yml.example
|
||||
|
||||
# git & gitlab related
|
||||
.git/
|
||||
@ -17,6 +18,8 @@ docker-compose.yml
|
||||
.venv
|
||||
.vscode
|
||||
crafty_commander.exe
|
||||
CHANGELOG.md
|
||||
CONTRIBUTING.md
|
||||
DBCHANGES.md
|
||||
docker-compose.yml.example
|
||||
README.md
|
||||
sonar-project.properties
|
||||
|
@ -57,3 +57,27 @@ pylint:
|
||||
reports:
|
||||
codequality: codeclimate.json
|
||||
when: always
|
||||
|
||||
# SonarQube/SonarCloud - Code Climate & QA [https://www.sonarsource.com]
|
||||
sonarcloud-check:
|
||||
stage: lint
|
||||
image:
|
||||
name: sonarsource/sonar-scanner-cli:latest
|
||||
entrypoint: [""]
|
||||
tags:
|
||||
- docker
|
||||
rules:
|
||||
- exists:
|
||||
- $SONAR_TOKEN
|
||||
- if: "$CODE_QUALITY_DISABLED"
|
||||
when: never
|
||||
- if: "$CI_COMMIT_TAG || $CI_COMMIT_BRANCH"
|
||||
variables:
|
||||
SONAR_USER_HOME: "${CI_PROJECT_DIR}/.sonar" # Defines the location of the analysis task cache
|
||||
GIT_DEPTH: "0" # Tells git to fetch all the branches of the project, required by the analysis task
|
||||
cache:
|
||||
key: "${CI_JOB_NAME}"
|
||||
paths:
|
||||
- .sonar/cache
|
||||
script:
|
||||
- sonar-scanner
|
||||
|
18
CHANGELOG.md
18
CHANGELOG.md
@ -1,17 +1,33 @@
|
||||
# Changelog
|
||||
## --- [4.1.2] - 2023/TBD
|
||||
## --- [4.1.3] - 2023/TBD
|
||||
### New features
|
||||
TBD
|
||||
### Bug fixes
|
||||
- Include tzdata in Docker image ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/604))
|
||||
- Fix text/formatting issue on server config page ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/602))
|
||||
- Bump required version of PyYAML to 6.0.1 ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/609))
|
||||
- Fix enable/disable schedule toggles on schedule list ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/606))
|
||||
### Refactor
|
||||
- Replace "in_file" helper method ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/605))
|
||||
### Tweaks
|
||||
- Add public status link to login ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/608))
|
||||
### Lang
|
||||
TBD
|
||||
<br><br>
|
||||
|
||||
## --- [4.1.2] - 2023/06/18
|
||||
### Bug fixes
|
||||
- Fix upload root files being hidden ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/590))
|
||||
- Send empty json for no banned/cached players ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/589))
|
||||
- Bump Tornado from 6.0 to 6.3.2 in response to CVE-2023-28370 ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/591))
|
||||
- Fix bug where commands would show "command_server" when initially created ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/592))
|
||||
- Add ID autofield to management CraftySettings class ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/599))
|
||||
### Refactor
|
||||
- Optimize player management page ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/593))
|
||||
### Tweaks
|
||||
- Remove bedrock servers in serverjars options ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/595))
|
||||
- Bump cryptography & pyOpenSSL ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/596))
|
||||
- Bump requests ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/600))
|
||||
### Lang
|
||||
- Update es_ES & pl_PL lang, thank you `.lucyy_` & `terrariadlc` ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/597))
|
||||
<br><br>
|
||||
|
@ -25,6 +25,7 @@ RUN apt-get update \
|
||||
openjdk-8-jre-headless \
|
||||
openjdk-11-jre-headless \
|
||||
openjdk-17-jre-headless \
|
||||
tzdata \
|
||||
&& apt-get autoremove \
|
||||
&& apt-get clean
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
[![Crafty Logo](app/frontend/static/assets/images/logo_long.svg)](https://craftycontrol.com)
|
||||
# Crafty Controller 4.1.2
|
||||
# Crafty Controller 4.1.3
|
||||
> Python based Control Panel for your Minecraft Server
|
||||
|
||||
## What is Crafty Controller?
|
||||
|
@ -43,6 +43,7 @@ class AuditLog(BaseModel):
|
||||
# Crafty Settings Class
|
||||
# **********************************************************************************
|
||||
class CraftySettings(BaseModel):
|
||||
id = AutoField()
|
||||
secret_api_key = CharField(default="")
|
||||
cookie_secret = CharField(default="")
|
||||
login_photo = CharField(default="login_1.jpg")
|
||||
|
@ -507,9 +507,9 @@ class Helpers:
|
||||
|
||||
return mounts
|
||||
|
||||
def is_subdir(self, server_path, root_dir):
|
||||
server_path = os.path.realpath(server_path)
|
||||
root_dir = os.path.realpath(root_dir)
|
||||
def is_subdir(self, child_path, parent_path):
|
||||
server_path = os.path.realpath(child_path)
|
||||
root_dir = os.path.realpath(parent_path)
|
||||
|
||||
if self.is_os_windows():
|
||||
try:
|
||||
@ -1244,22 +1244,6 @@ class Helpers:
|
||||
return temp_dir
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def in_path(parent_path, child_path):
|
||||
# Smooth out relative path names, note: if you are concerned about
|
||||
# symbolic links, you should use os.path.realpath too
|
||||
parent_path = os.path.abspath(parent_path)
|
||||
child_path = os.path.abspath(child_path)
|
||||
|
||||
# Compare the common path of the parent and child path with the
|
||||
# common path of just the parent path. Using the commonpath method
|
||||
# on just the parent path will regularise the path name in the same way
|
||||
# as the comparison that deals with both paths, removing any trailing
|
||||
# path separator
|
||||
return os.path.commonpath([parent_path]) == os.path.commonpath(
|
||||
[parent_path, child_path]
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def download_file(executable_url, jar_path):
|
||||
try:
|
||||
|
@ -656,12 +656,12 @@ class AjaxHandler(BaseHandler):
|
||||
|
||||
server_info = self.controller.servers.get_server_data_by_id(server_id)
|
||||
if not (
|
||||
Helpers.in_path(
|
||||
Helpers.get_os_understandable_path(server_info["path"]), file_path
|
||||
self.helper.is_subdir(
|
||||
file_path, Helpers.get_os_understandable_path(server_info["path"])
|
||||
)
|
||||
or Helpers.in_path(
|
||||
Helpers.get_os_understandable_path(server_info["backup_path"]),
|
||||
or self.helper.is_subdir(
|
||||
file_path,
|
||||
Helpers.get_os_understandable_path(server_info["backup_path"]),
|
||||
)
|
||||
) or not Helpers.check_file_exists(os.path.abspath(file_path)):
|
||||
logger.warning(f"Invalid path in del_backup ajax call ({file_path})")
|
||||
|
@ -57,11 +57,11 @@ class FileHandler(BaseHandler):
|
||||
return
|
||||
server_id = bleach.clean(server_id)
|
||||
|
||||
if not Helpers.in_path(
|
||||
if not self.helper.is_subdir(
|
||||
file_path,
|
||||
Helpers.get_os_understandable_path(
|
||||
self.controller.servers.get_server_data_by_id(server_id)["path"]
|
||||
),
|
||||
file_path,
|
||||
) or not Helpers.check_file_exists(os.path.abspath(file_path)):
|
||||
logger.warning(
|
||||
f"Invalid path in get_file file file ajax call ({file_path})"
|
||||
@ -163,11 +163,11 @@ class FileHandler(BaseHandler):
|
||||
return
|
||||
server_id = bleach.clean(server_id)
|
||||
|
||||
if not Helpers.in_path(
|
||||
if not self.helper.is_subdir(
|
||||
file_path,
|
||||
Helpers.get_os_understandable_path(
|
||||
self.controller.servers.get_server_data_by_id(server_id)["path"]
|
||||
),
|
||||
file_path,
|
||||
) or Helpers.check_file_exists(os.path.abspath(file_path)):
|
||||
logger.warning(
|
||||
f"Invalid path in create_file file ajax call ({file_path})"
|
||||
@ -196,11 +196,11 @@ class FileHandler(BaseHandler):
|
||||
return
|
||||
server_id = bleach.clean(server_id)
|
||||
|
||||
if not Helpers.in_path(
|
||||
if not self.helper.is_subdir(
|
||||
dir_path,
|
||||
Helpers.get_os_understandable_path(
|
||||
self.controller.servers.get_server_data_by_id(server_id)["path"]
|
||||
),
|
||||
dir_path,
|
||||
) or Helpers.check_path_exists(os.path.abspath(dir_path)):
|
||||
logger.warning(
|
||||
f"Invalid path in create_dir file ajax call ({dir_path})"
|
||||
@ -263,12 +263,12 @@ class FileHandler(BaseHandler):
|
||||
|
||||
server_info = self.controller.servers.get_server_data_by_id(server_id)
|
||||
if not (
|
||||
Helpers.in_path(
|
||||
Helpers.get_os_understandable_path(server_info["path"]), file_path
|
||||
self.helper.is_subdir(
|
||||
file_path, Helpers.get_os_understandable_path(server_info["path"])
|
||||
)
|
||||
or Helpers.in_path(
|
||||
Helpers.get_os_understandable_path(server_info["backup_path"]),
|
||||
or self.helper.is_subdir(
|
||||
file_path,
|
||||
Helpers.get_os_understandable_path(server_info["backup_path"]),
|
||||
)
|
||||
) or not Helpers.check_file_exists(os.path.abspath(file_path)):
|
||||
logger.warning(f"Invalid path in del_file file ajax call ({file_path})")
|
||||
@ -296,8 +296,8 @@ class FileHandler(BaseHandler):
|
||||
server_id = bleach.clean(server_id)
|
||||
|
||||
server_info = self.controller.servers.get_server_data_by_id(server_id)
|
||||
if not Helpers.in_path(
|
||||
Helpers.get_os_understandable_path(server_info["path"]), dir_path
|
||||
if not self.helper.is_subdir(
|
||||
dir_path, Helpers.get_os_understandable_path(server_info["path"])
|
||||
) or not Helpers.check_path_exists(os.path.abspath(dir_path)):
|
||||
logger.warning(f"Invalid path in del_file file ajax call ({dir_path})")
|
||||
Console.warning(f"Invalid path in del_file file ajax call ({dir_path})")
|
||||
@ -348,11 +348,11 @@ class FileHandler(BaseHandler):
|
||||
return
|
||||
server_id = bleach.clean(server_id)
|
||||
|
||||
if not Helpers.in_path(
|
||||
if not self.helper.is_subdir(
|
||||
file_path,
|
||||
Helpers.get_os_understandable_path(
|
||||
self.controller.servers.get_server_data_by_id(server_id)["path"]
|
||||
),
|
||||
file_path,
|
||||
) or not Helpers.check_file_exists(os.path.abspath(file_path)):
|
||||
logger.warning(
|
||||
f"Invalid path in save_file file ajax call ({file_path})"
|
||||
@ -366,60 +366,6 @@ class FileHandler(BaseHandler):
|
||||
with open(file_path, "w", encoding="utf-8") as file_object:
|
||||
file_object.write(file_contents)
|
||||
|
||||
elif page == "rename_file":
|
||||
if not permissions["Files"] in user_perms:
|
||||
if not superuser:
|
||||
self.redirect("/panel/error?error=Unauthorized access to Files")
|
||||
return
|
||||
item_path = Helpers.get_os_understandable_path(
|
||||
self.get_body_argument("item_path", default=None, strip=True)
|
||||
)
|
||||
new_item_name = self.get_body_argument(
|
||||
"new_item_name", default=None, strip=True
|
||||
)
|
||||
|
||||
if not self.check_server_id(server_id, "rename_file"):
|
||||
return
|
||||
server_id = bleach.clean(server_id)
|
||||
|
||||
if item_path is None or new_item_name is None:
|
||||
logger.warning("Invalid path(s) in rename_file file ajax call")
|
||||
Console.warning("Invalid path(s) in rename_file file ajax call")
|
||||
return
|
||||
|
||||
if not Helpers.in_path(
|
||||
Helpers.get_os_understandable_path(
|
||||
self.controller.servers.get_server_data_by_id(server_id)["path"]
|
||||
),
|
||||
item_path,
|
||||
) or not Helpers.check_path_exists(os.path.abspath(item_path)):
|
||||
logger.warning(
|
||||
f"Invalid old name path in rename_file file ajax call ({server_id})"
|
||||
)
|
||||
Console.warning(
|
||||
f"Invalid old name path in rename_file file ajax call ({server_id})"
|
||||
)
|
||||
return
|
||||
|
||||
new_item_path = os.path.join(os.path.split(item_path)[0], new_item_name)
|
||||
|
||||
if not Helpers.in_path(
|
||||
Helpers.get_os_understandable_path(
|
||||
self.controller.servers.get_server_data_by_id(server_id)["path"]
|
||||
),
|
||||
new_item_path,
|
||||
) or Helpers.check_path_exists(os.path.abspath(new_item_path)):
|
||||
logger.warning(
|
||||
f"Invalid new name path in rename_file file ajax call ({server_id})"
|
||||
)
|
||||
Console.warning(
|
||||
f"Invalid new name path in rename_file file ajax call ({server_id})"
|
||||
)
|
||||
return
|
||||
|
||||
# RENAME
|
||||
os.rename(item_path, new_item_path)
|
||||
|
||||
@tornado.web.authenticated
|
||||
def patch(self, page):
|
||||
api_key, _, exec_user = self.current_user
|
||||
@ -462,11 +408,11 @@ class FileHandler(BaseHandler):
|
||||
Console.warning("Invalid path(s) in rename_file file ajax call")
|
||||
return
|
||||
|
||||
if not Helpers.in_path(
|
||||
if not self.helper.is_subdir(
|
||||
item_path,
|
||||
Helpers.get_os_understandable_path(
|
||||
self.controller.servers.get_server_data_by_id(server_id)["path"]
|
||||
),
|
||||
item_path,
|
||||
) or not Helpers.check_path_exists(os.path.abspath(item_path)):
|
||||
logger.warning(
|
||||
f"Invalid old name path in rename_file file ajax call ({server_id})"
|
||||
@ -478,11 +424,11 @@ class FileHandler(BaseHandler):
|
||||
|
||||
new_item_path = os.path.join(os.path.split(item_path)[0], new_item_name)
|
||||
|
||||
if not Helpers.in_path(
|
||||
if not self.helper.is_subdir(
|
||||
new_item_path,
|
||||
Helpers.get_os_understandable_path(
|
||||
self.controller.servers.get_server_data_by_id(server_id)["path"]
|
||||
),
|
||||
new_item_path,
|
||||
) or Helpers.check_path_exists(os.path.abspath(new_item_path)):
|
||||
logger.warning(
|
||||
f"Invalid new name path in rename_file file ajax call ({server_id})"
|
||||
|
@ -808,9 +808,9 @@ class PanelHandler(BaseHandler):
|
||||
Helpers.get_os_understandable_path(server_info["backup_path"]), file
|
||||
)
|
||||
)
|
||||
if not Helpers.in_path(
|
||||
Helpers.get_os_understandable_path(server_info["backup_path"]),
|
||||
if not self.helper.is_subdir(
|
||||
backup_file,
|
||||
Helpers.get_os_understandable_path(server_info["backup_path"]),
|
||||
) or not os.path.isfile(backup_file):
|
||||
self.redirect("/panel/error?error=Invalid path detected")
|
||||
return
|
||||
@ -1463,8 +1463,9 @@ class PanelHandler(BaseHandler):
|
||||
|
||||
server_info = self.controller.servers.get_server_data_by_id(server_id)
|
||||
|
||||
if not Helpers.in_path(
|
||||
Helpers.get_os_understandable_path(server_info["path"]), file
|
||||
if not self.helper.is_subdir(
|
||||
file,
|
||||
Helpers.get_os_understandable_path(server_info["path"]),
|
||||
) or not os.path.isfile(file):
|
||||
self.redirect("/panel/error?error=Invalid path detected")
|
||||
return
|
||||
|
@ -92,20 +92,21 @@ class ApiServersServerTasksIndexHandler(BaseApiHandler):
|
||||
data["start_time"] = "00:00"
|
||||
|
||||
# validate cron string
|
||||
if data["cron_string"] != "" and not croniter.is_valid(data["cron_string"]):
|
||||
return self.finish_json(
|
||||
405,
|
||||
{
|
||||
"status": "error",
|
||||
"error": self.helper.translation.translate(
|
||||
"error",
|
||||
"cronFormat",
|
||||
self.controller.users.get_user_lang_by_id(
|
||||
auth_data[4]["user_id"]
|
||||
if "cron_string" in data:
|
||||
if data["cron_string"] != "" and not croniter.is_valid(data["cron_string"]):
|
||||
return self.finish_json(
|
||||
405,
|
||||
{
|
||||
"status": "error",
|
||||
"error": self.helper.translation.translate(
|
||||
"error",
|
||||
"cronFormat",
|
||||
self.controller.users.get_user_lang_by_id(
|
||||
auth_data[4]["user_id"]
|
||||
),
|
||||
),
|
||||
),
|
||||
},
|
||||
)
|
||||
},
|
||||
)
|
||||
if "parent" not in data:
|
||||
data["parent"] = None
|
||||
task_id = self.tasks_manager.schedule_job(data)
|
||||
|
@ -136,20 +136,21 @@ class ApiServersServerTasksTaskIndexHandler(BaseApiHandler):
|
||||
data["parent"] = None
|
||||
|
||||
data["server_id"] = server_id
|
||||
if data["cron_string"] != "" and not croniter.is_valid(data["cron_string"]):
|
||||
return self.finish_json(
|
||||
405,
|
||||
{
|
||||
"status": "error",
|
||||
"error": self.helper.translation.translate(
|
||||
"error",
|
||||
"cronFormat",
|
||||
self.controller.users.get_user_lang_by_id(
|
||||
auth_data[4]["user_id"]
|
||||
if "cron_string" in data:
|
||||
if data["cron_string"] != "" and not croniter.is_valid(data["cron_string"]):
|
||||
return self.finish_json(
|
||||
405,
|
||||
{
|
||||
"status": "error",
|
||||
"error": self.helper.translation.translate(
|
||||
"error",
|
||||
"cronFormat",
|
||||
self.controller.users.get_user_lang_by_id(
|
||||
auth_data[4]["user_id"]
|
||||
),
|
||||
),
|
||||
),
|
||||
},
|
||||
)
|
||||
},
|
||||
)
|
||||
self.tasks_manager.update_job(task_id, data)
|
||||
|
||||
self.controller.management.add_to_audit_log(
|
||||
|
@ -278,11 +278,11 @@ class UploadHandler(BaseHandler):
|
||||
filename = self.request.headers.get("X-FileName", None)
|
||||
full_path = os.path.join(path, filename)
|
||||
|
||||
if not Helpers.in_path(
|
||||
if not self.helper.is_subdir(
|
||||
full_path,
|
||||
Helpers.get_os_understandable_path(
|
||||
self.controller.servers.get_server_data_by_id(server_id)["path"]
|
||||
),
|
||||
full_path,
|
||||
):
|
||||
logger.warning(
|
||||
f"User {user_id} tried to upload a file to {server_id} "
|
||||
|
@ -1,5 +1,5 @@
|
||||
{
|
||||
"major": 4,
|
||||
"minor": 1,
|
||||
"sub": 2
|
||||
"sub": 3
|
||||
}
|
||||
|
@ -166,48 +166,6 @@
|
||||
<input type="number" class="form-control" name="logs_delete_after" id="logs_delete_after" value="{{ data['server_stats']['server_id']['logs_delete_after'] }}" step="1" max="365" min="0" required>
|
||||
</div>
|
||||
|
||||
<!--
|
||||
<div class="form-check-flat">
|
||||
<label for="auto_start" class="form-check-label ml-4 mb-4">
|
||||
{% if data['server_stats']['server_id']['auto_start'] %}
|
||||
<input type="checkbox" class="form-check-input" id="auto_start" name="auto_start" checked=""
|
||||
data-toggle="toggle" value="1"> {{ translate('serverConfig', 'serverAutoStart',
|
||||
data['lang']) }}
|
||||
{% else %}
|
||||
<input type="checkbox" class="form-check-input" id="auto_start" name="auto_start" value="1"
|
||||
data-toggle="toggle"> {{
|
||||
translate('serverConfig', 'serverAutoStart', data['lang']) }}
|
||||
{% end %}
|
||||
</label>
|
||||
|
||||
<label for="crash_detection" class="form-check-label ml-4 mb-4">
|
||||
{% if data['server_stats']['server_id']['crash_detection'] %}
|
||||
<input type="checkbox" class="form-check-input" id="crash_detection" name="crash_detection"
|
||||
data-toggle="toggle" checked="" value="1"> {{ translate('serverConfig',
|
||||
'serverCrashDetection', data['lang']) }}
|
||||
{% else %}
|
||||
<input type="checkbox" class="form-check-input" id="crash_detection" name="crash_detection"
|
||||
data-toggle="toggle" value="1"> {{ translate('serverConfig', 'serverCrashDetection',
|
||||
data['lang']) }}
|
||||
{% end %}
|
||||
</label>
|
||||
|
||||
{% if data['super_user'] %}
|
||||
<label for="show_status" class="form-check-label ml-4 mb-4">
|
||||
{% if data['server_stats']['server_id']['show_status'] %}
|
||||
<input type="checkbox" class="form-check-input" id="show_status" name="show_status"
|
||||
data-toggle="toggle" checked="" value="1"> {{ translate('serverConfig', 'showStatus',
|
||||
data['lang']) }}
|
||||
{% else %}
|
||||
<input type="checkbox" class="form-check-input" id="show_status" name="show_status"
|
||||
data-toggle="toggle" value="1"> {{ translate('serverConfig', 'showStatus',
|
||||
data['lang']) }}
|
||||
{% end %}
|
||||
</label>
|
||||
{% end %}
|
||||
</div>
|
||||
-->
|
||||
|
||||
<div class="form-group">
|
||||
<div class="custom-control custom-switch">
|
||||
{% if data['server_stats']['server_id']['auto_start'] %}
|
||||
@ -237,7 +195,7 @@
|
||||
<input type="checkbox" class="custom-control-input" id="show_status" name="show_status" checked="" value="1">
|
||||
<label class="custom-control-label" for="show_status"> {{ translate('serverConfig', 'showStatus', data['lang']) }}</label>
|
||||
{% else %}
|
||||
<input type="checkbox" class="custom-control-input" id="show_status" name="show_status" value="1"> {{ translate('serverConfig', 'showStatus', data['lang']) }}
|
||||
<input type="checkbox" class="custom-control-input" id="show_status" name="show_status" value="1">
|
||||
<label class="custom-control-label" for="show_status"> {{ translate('serverConfig', 'showStatus', data['lang']) }}</label>
|
||||
{% end %}
|
||||
{% end %}
|
||||
|
@ -120,6 +120,11 @@
|
||||
<span class="text-small font-weight-semibold"><a href="https://craftycontrol.com/">Crafty Control
|
||||
{{data['version'] }}</a> </span>
|
||||
</div>
|
||||
|
||||
<div class="text-block text-center my-3">
|
||||
<a href="/status" class="text-small forgot-password">{{ translate('login', 'viewStatus',
|
||||
data['lang']) }}</a>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
|
@ -203,7 +203,8 @@
|
||||
"forgotPassword": "Forgot Password",
|
||||
"login": "Log In",
|
||||
"password": "Password",
|
||||
"username": "Username"
|
||||
"username": "Username",
|
||||
"viewStatus": "View Public Status Page"
|
||||
},
|
||||
"notify": {
|
||||
"activityLog": "Activity Logs",
|
||||
|
@ -12,8 +12,8 @@ pexpect==4.8
|
||||
psutil==5.9
|
||||
pyOpenSSL==23.2.0
|
||||
pyjwt==2.4.0
|
||||
PyYAML==5.4
|
||||
requests==2.26
|
||||
PyYAML==6.0.1
|
||||
requests==2.31
|
||||
termcolor==1.1
|
||||
tornado==6.3.2
|
||||
tzlocal==4.0
|
||||
|
14
sonar-project.properties
Normal file
14
sonar-project.properties
Normal file
@ -0,0 +1,14 @@
|
||||
sonar.projectKey=crafty-controller_crafty-4
|
||||
sonar.organization=crafty-controller
|
||||
|
||||
# This is the name and version displayed in the SonarCloud UI.
|
||||
sonar.projectName=Crafty 4
|
||||
sonar.projectVersion=4.1.3
|
||||
sonar.python.version=3.9, 3.10, 3.11
|
||||
sonar.exclusions=app/migrations/**, app/frontend/static/assets/vendors/**
|
||||
|
||||
# Path is relative to the sonar-project.properties file. Replace "\" by "/" on Windows.
|
||||
#sonar.sources=.
|
||||
|
||||
# Encoding of the source code. Default is default system encoding
|
||||
#sonar.sourceEncoding=UTF-8
|
Loading…
x
Reference in New Issue
Block a user