mirror of
https://gitlab.com/crafty-controller/crafty-4.git
synced 2025-01-18 09:05:15 +01:00
Merge branch 'dev' into sec/sanitize-image
This commit is contained in:
commit
aeef4a1e12
1
.gitignore
vendored
1
.gitignore
vendored
@ -38,3 +38,4 @@ docker/*
|
||||
!docker/docker-compose.yml
|
||||
lang_sort_log.txt
|
||||
lang_sort.txt
|
||||
app/migrations/status
|
||||
|
14
CHANGELOG.md
14
CHANGELOG.md
@ -1,16 +1,24 @@
|
||||
# Changelog
|
||||
## --- [4.4.4] - 2024/TBD
|
||||
## --- [4.4.5] - 2024/TBD
|
||||
### New features
|
||||
TBD
|
||||
### Bug fixes
|
||||
- Fix logic issue causing bedrock wizard's root files buttons to not respond to user click events ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/797))
|
||||
- Reset crash detection counter after crash detection process detects successful start ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/798))
|
||||
TBD
|
||||
### Tweaks
|
||||
TBD
|
||||
### Lang
|
||||
TBD
|
||||
<br><br>
|
||||
|
||||
## --- [4.4.4] - 2024/10/03
|
||||
### Bug fixes
|
||||
- Migrations | Fix orphan schedule configurations crashing migration operation ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/796))
|
||||
- Fix logic issue causing bedrock wizard's root files buttons to not respond to user click events ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/797))
|
||||
- Reset crash detection counter after crash detection process detects successful start ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/798))
|
||||
- Update new bedrock DL url and correctly bubble up exception on DL fail - Thanks @sarcastron ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/802))
|
||||
- Bump cryptography for GHSA-h4gh-qq45-vh27 ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/803))
|
||||
<br><br>
|
||||
|
||||
## --- [4.4.3] - 2024/08/08
|
||||
### Bug fixes
|
||||
- Fix schedules creation fail due to missing action ID ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/791))
|
||||
|
@ -1,5 +1,5 @@
|
||||
[![Crafty Logo](app/frontend/static/assets/images/logo_long.svg)](https://craftycontrol.com)
|
||||
# Crafty Controller 4.4.4
|
||||
# Crafty Controller 4.4.5
|
||||
> Python based Control Panel for your Minecraft Server
|
||||
|
||||
## What is Crafty Controller?
|
||||
|
@ -58,6 +58,7 @@ class Helpers:
|
||||
|
||||
def __init__(self):
|
||||
self.root_dir = os.path.abspath(os.path.curdir)
|
||||
self.read_annc = False
|
||||
self.config_dir = os.path.join(self.root_dir, "app", "config")
|
||||
self.webroot = os.path.join(self.root_dir, "app", "frontend")
|
||||
self.servers_dir = os.path.join(self.root_dir, "servers")
|
||||
@ -79,6 +80,7 @@ class Helpers:
|
||||
|
||||
self.translation = Translation(self)
|
||||
self.update_available = False
|
||||
self.migration_notifications = []
|
||||
self.ignored_names = ["crafty_managed.txt", "db_stats"]
|
||||
self.crafty_starting = False
|
||||
self.minimum_password_length = 8
|
||||
@ -128,24 +130,33 @@ class Helpers:
|
||||
"Chrome/104.0.0.0 Safari/537.36"
|
||||
),
|
||||
}
|
||||
target_win = 'https://minecraft.azureedge.net/bin-win/[^"]*'
|
||||
target_linux = 'https://minecraft.azureedge.net/bin-linux/[^"]*'
|
||||
|
||||
target_win = 'https://www.minecraft.net/bedrockdedicatedserver/bin-win/[^"]*'
|
||||
target_linux = (
|
||||
'https://www.minecraft.net/bedrockdedicatedserver/bin-linux/[^"]*'
|
||||
)
|
||||
try:
|
||||
# Get minecraft server download page
|
||||
# (hopefully the don't change the structure)
|
||||
download_page = get(url, headers=headers, timeout=1)
|
||||
|
||||
download_page.raise_for_status()
|
||||
# Search for our string targets
|
||||
win_download_url = re.search(target_win, download_page.text).group(0)
|
||||
linux_download_url = re.search(target_linux, download_page.text).group(0)
|
||||
win_search_result = re.search(target_win, download_page.text)
|
||||
linux_search_result = re.search(target_linux, download_page.text)
|
||||
if win_search_result is None or linux_search_result is None:
|
||||
raise RuntimeError(
|
||||
"Could not determine download URL from minecraft.net."
|
||||
)
|
||||
|
||||
win_download_url = win_search_result.group(0)
|
||||
linux_download_url = linux_search_result.group(0)
|
||||
print(win_download_url, linux_download_url)
|
||||
if os.name == "nt":
|
||||
return win_download_url
|
||||
|
||||
return linux_download_url
|
||||
except Exception as e:
|
||||
logger.error(f"Unable to resolve remote bedrock download url! \n{e}")
|
||||
raise e
|
||||
return False
|
||||
|
||||
def get_execution_java(self, value, execution_command):
|
||||
@ -614,11 +625,49 @@ class Helpers:
|
||||
|
||||
return version_data
|
||||
|
||||
def get_announcements(self):
|
||||
def check_migrations(self) -> None:
|
||||
if self.read_annc is False:
|
||||
self.read_annc = True
|
||||
for file in os.listdir(
|
||||
os.path.join(self.root_dir, "app", "migrations", "status")
|
||||
):
|
||||
with open(
|
||||
os.path.join(self.root_dir, "app", "migrations", "status", file),
|
||||
"r",
|
||||
encoding="utf-8",
|
||||
) as notif_file:
|
||||
file_json = json.load(notif_file)
|
||||
for notif in file_json:
|
||||
if not file_json[notif].get("status"):
|
||||
self.migration_notifications.append(file_json[notif])
|
||||
|
||||
def get_announcements(self, lang=None):
|
||||
try:
|
||||
data = []
|
||||
response = requests.get("https://craftycontrol.com/notify", timeout=2)
|
||||
data = json.loads(response.content)
|
||||
if not lang:
|
||||
lang = self.get_setting("language")
|
||||
self.check_migrations()
|
||||
for migration_warning in self.migration_notifications:
|
||||
if not migration_warning.get("status"):
|
||||
data.append(
|
||||
{
|
||||
"id": migration_warning.get("pid"),
|
||||
"title": self.translation.translate(
|
||||
"notify",
|
||||
f"{migration_warning.get('type')}_title",
|
||||
lang,
|
||||
),
|
||||
"date": "",
|
||||
"desc": self.translation.translate(
|
||||
"notify",
|
||||
f"{migration_warning.get('type')}_desc",
|
||||
lang,
|
||||
),
|
||||
"link": "",
|
||||
}
|
||||
)
|
||||
if self.update_available:
|
||||
data.append(self.update_available)
|
||||
return data
|
||||
|
@ -217,15 +217,16 @@ class ImportHelpers:
|
||||
FileHelpers.del_dirs(temp_dir)
|
||||
|
||||
def download_bedrock_server(self, path, new_id):
|
||||
bedrock_url = Helpers.get_latest_bedrock_url()
|
||||
download_thread = threading.Thread(
|
||||
target=self.download_threaded_bedrock_server,
|
||||
daemon=True,
|
||||
args=(path, new_id),
|
||||
args=(path, new_id, bedrock_url),
|
||||
name=f"{new_id}_download",
|
||||
)
|
||||
download_thread.start()
|
||||
|
||||
def download_threaded_bedrock_server(self, path, new_id):
|
||||
def download_threaded_bedrock_server(self, path, new_id, bedrock_url):
|
||||
"""
|
||||
Downloads the latest Bedrock server, unzips it, sets necessary permissions.
|
||||
|
||||
@ -236,10 +237,8 @@ class ImportHelpers:
|
||||
This method handles exceptions and logs errors for each step of the process.
|
||||
"""
|
||||
try:
|
||||
bedrock_url = Helpers.get_latest_bedrock_url()
|
||||
if bedrock_url:
|
||||
file_path = os.path.join(path, "bedrock_server.zip")
|
||||
|
||||
success = FileHelpers.ssl_get_file(
|
||||
bedrock_url, path, "bedrock_server.zip"
|
||||
)
|
||||
@ -263,6 +262,7 @@ class ImportHelpers:
|
||||
logger.critical(
|
||||
f"Failed to download bedrock executable during server creation! \n{e}"
|
||||
)
|
||||
raise e
|
||||
|
||||
ServersController.finish_import(new_id)
|
||||
server_users = PermissionsServers.get_server_user_list(new_id)
|
||||
|
@ -29,7 +29,7 @@ class ApiAnnounceIndexHandler(BaseApiHandler):
|
||||
_,
|
||||
) = auth_data
|
||||
|
||||
data = self.helper.get_announcements()
|
||||
data = self.helper.get_announcements(auth_data[4]["lang"])
|
||||
if not data:
|
||||
return self.finish_json(
|
||||
424,
|
||||
|
@ -725,7 +725,19 @@ class ApiServersIndexHandler(BaseApiHandler):
|
||||
405, {"status": "error", "error": "DATA CONSTRAINT FAILED"}
|
||||
)
|
||||
return
|
||||
new_server_id = self.controller.create_api_server(data, user["user_id"])
|
||||
try:
|
||||
new_server_id = self.controller.create_api_server(data, user["user_id"])
|
||||
except Exception as e:
|
||||
self.controller.servers.stats.record_stats()
|
||||
|
||||
self.finish_json(
|
||||
503,
|
||||
{
|
||||
"status": "error",
|
||||
"error": "Could not create server",
|
||||
"error_data": str(e),
|
||||
},
|
||||
)
|
||||
|
||||
self.controller.servers.stats.record_stats()
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
{
|
||||
"major": 4,
|
||||
"minor": 4,
|
||||
"sub": 4
|
||||
"sub": 5
|
||||
}
|
||||
|
@ -619,7 +619,9 @@
|
||||
if (responseData.status === "ok") {
|
||||
window.location.href = '/panel/dashboard';
|
||||
} else {
|
||||
|
||||
// Close the "be patient..." dialogue box
|
||||
$('.bootbox-close-button').click();
|
||||
// Alert the user that there was an issue.
|
||||
bootbox.alert({
|
||||
title: responseData.error,
|
||||
message: responseData.error_data
|
||||
@ -778,4 +780,4 @@
|
||||
</script>
|
||||
<script type="text/javascript" src="../../static/assets/js/shared/root-dir.js"></script>
|
||||
<script src="../../static/assets/js/shared/bootstrap-select-1.13.18.js"></script>
|
||||
{% end %}
|
||||
{% end %}
|
||||
|
@ -1,4 +1,5 @@
|
||||
import os
|
||||
import json
|
||||
import datetime
|
||||
import uuid
|
||||
import peewee
|
||||
@ -13,9 +14,9 @@ from app.classes.shared.file_helpers import FileHelpers
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def is_valid_backup(backup, all_servers):
|
||||
def is_valid_entry(entry, all_servers):
|
||||
try:
|
||||
return str(backup.server_id) in all_servers
|
||||
return str(entry.server_id) in all_servers
|
||||
except (TypeError, peewee.DoesNotExist):
|
||||
return False
|
||||
|
||||
@ -24,6 +25,8 @@ def migrate(migrator: Migrator, database, **kwargs):
|
||||
"""
|
||||
Write your migrations here.
|
||||
"""
|
||||
backup_migration_status = True
|
||||
schedule_migration_status = True
|
||||
db = database
|
||||
Console.info("Starting Backups migrations")
|
||||
Console.info(
|
||||
@ -161,10 +164,20 @@ def migrate(migrator: Migrator, database, **kwargs):
|
||||
row.server_id for row in Servers.select(Servers.server_id).distinct()
|
||||
]
|
||||
all_backups = Backups.select()
|
||||
all_schedules = Schedules.select()
|
||||
Console.info("Cleaning up orphan backups for all servers")
|
||||
valid_backups = [
|
||||
backup for backup in all_backups if is_valid_backup(backup, all_servers)
|
||||
backup for backup in all_backups if is_valid_entry(backup, all_servers)
|
||||
]
|
||||
if len(valid_backups) < len(all_backups):
|
||||
backup_migration_status = False
|
||||
print("Orphan backup found")
|
||||
Console.info("Cleaning up orphan schedules for all servers")
|
||||
valid_schedules = [
|
||||
schedule for schedule in all_schedules if is_valid_entry(schedule, all_servers)
|
||||
]
|
||||
if len(valid_schedules) < len(all_schedules):
|
||||
schedule_migration_status = False
|
||||
# Copy data from the existing backups table to the new one
|
||||
for backup in valid_backups:
|
||||
Console.info(f"Trying to get server for backup migration {backup.server_id}")
|
||||
@ -221,13 +234,20 @@ def migrate(migrator: Migrator, database, **kwargs):
|
||||
Console.debug("Migrations: Dropping backup_path from servers table")
|
||||
migrator.drop_columns("servers", ["backup_path"])
|
||||
|
||||
for schedule in Schedules.select():
|
||||
for schedule in valid_schedules:
|
||||
action_id = None
|
||||
if schedule.command == "backup_server":
|
||||
Console.info(
|
||||
f"Migrations: Adding backup ID to task with name {schedule.name}"
|
||||
)
|
||||
backup = NewBackups.get(NewBackups.server_id == schedule.server_id)
|
||||
try:
|
||||
backup = NewBackups.get(NewBackups.server_id == schedule.server_id)
|
||||
except:
|
||||
schedule_migration_status = False
|
||||
Console.error(
|
||||
"Could not find backup with selected server ID. Omitting from register."
|
||||
)
|
||||
continue
|
||||
action_id = backup.backup_id
|
||||
NewSchedules.create(
|
||||
schedule_id=schedule.schedule_id,
|
||||
@ -255,6 +275,34 @@ def migrate(migrator: Migrator, database, **kwargs):
|
||||
# Rename the new table to backups
|
||||
migrator.rename_table("new_schedules", "schedules")
|
||||
|
||||
with open(
|
||||
os.path.join(
|
||||
os.path.abspath(os.path.curdir),
|
||||
"app",
|
||||
"migrations",
|
||||
"status",
|
||||
"20240308_multi-backup.json",
|
||||
),
|
||||
"w",
|
||||
encoding="utf-8",
|
||||
) as file:
|
||||
file.write(
|
||||
json.dumps(
|
||||
{
|
||||
"backup_migration": {
|
||||
"type": "backup",
|
||||
"status": backup_migration_status,
|
||||
"pid": str(uuid.uuid4()),
|
||||
},
|
||||
"schedule_migration": {
|
||||
"type": "schedule",
|
||||
"status": schedule_migration_status,
|
||||
"pid": str(uuid.uuid4()),
|
||||
},
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def rollback(migrator: Migrator, database, **kwargs):
|
||||
"""
|
||||
|
@ -231,10 +231,14 @@
|
||||
"activityLog": "Activity Logs",
|
||||
"backupComplete": "Backup completed successfully for server {}",
|
||||
"backupStarted": "Backup started for server {}",
|
||||
"backup_desc": "We detected the backup migration may have partially or fully failed. Please confirm your backups records on the backups tab.",
|
||||
"backup_title": "Backup Migration Warning",
|
||||
"downloadLogs": "Download Support Logs?",
|
||||
"finishedPreparing": "We've finished preparing your support logs. Please click download to download",
|
||||
"logout": "Logout",
|
||||
"preparingLogs": " Please wait while we prepare your logs... We`ll send a notification when they`re ready. This may take a while for large deployments.",
|
||||
"schedule_desc": "We detected some or all of your scheduled tasks were not successfully transfered during the upgrade. Please confirm your schedules in the schedules tab.",
|
||||
"schedule_title": "Schedules Migration Warning",
|
||||
"supportLogs": "Support Logs"
|
||||
},
|
||||
"offline": {
|
||||
|
@ -235,10 +235,14 @@
|
||||
"activityLog": "Aktivitātes Logi",
|
||||
"backupComplete": "Dublējums veiksmīgi pabeigts priekš servera ",
|
||||
"backupStarted": "Dublējums startēts priekš servera ",
|
||||
"backup_desc": "Mēs noteicām ka dublējuma migrācija daļēji vai pilnīgi neizdevās. Lūdzu pārskatiet savus dublējumus dublējumu cilnē.",
|
||||
"backup_title": "Dublējuma Migrācijas Brīdinājums",
|
||||
"downloadLogs": "Lejupielādēt Atbalsta Log Failus?",
|
||||
"finishedPreparing": "Mēs esam pabeiguši sagatavot jūsu atbalsta log datnes. Lūdzu nospiediet lejupielādet lai lejupielādētu",
|
||||
"logout": "Iziet",
|
||||
"preparingLogs": " Lūdzu uzgaidiet kamēr mēs sagatavojam jūsu log datnes... Mēs jums nosūtīsim paziņojumu kad tās būs gatavas. Tas var aizņemt kādu laiku priekš lielām instalācijām.",
|
||||
"schedule_desc": "Mēs noteicām ka daži vai visi no jūsu darbību grafikiem nebija veiksmīgi pārnesti atjauninājuma laikā. Lūdzu pārskatiet savus grafikus grafiku cilnē.",
|
||||
"schedule_title": "Grafiku Migrācijas Brīdinājums",
|
||||
"supportLogs": "Atbalsta Logi"
|
||||
},
|
||||
"offline": {
|
||||
|
@ -234,10 +234,14 @@
|
||||
"activityLog": "Activiteitslogboeken",
|
||||
"backupComplete": "Back-up succesvol voltooid voor server {}",
|
||||
"backupStarted": "Backup gestart voor server {}",
|
||||
"backup_desc": "We hebben gedetecteerd dat de back-upmigratie mogelijk gedeeltelijk of volledig is mislukt. Controleer uw back-uprecords op het tabblad Backups.",
|
||||
"backup_title": "Waarschuwing voor back-upmigratie",
|
||||
"downloadLogs": "Ondersteuningslogboeken downloaden?",
|
||||
"finishedPreparing": "We zijn klaar met het voorbereiden van uw ondersteuningslogboeken. Klik op download om te downloaden",
|
||||
"logout": "Uitloggen",
|
||||
"preparingLogs": " Een ogenblik geduld alstublieft terwijl wij uw logboeken voorbereiden... We sturen een bericht als ze klaar zijn. Dit kan een tijdje duren voor grote implementaties.",
|
||||
"schedule_desc": "We hebben gedetecteerd dat sommige of alle geplande taken niet succesvol zijn overgedragen tijdens de upgrade. Controleer uw schema's op het tabblad Schema's.",
|
||||
"schedule_title": "Waarschuwing voor schemamigratie",
|
||||
"supportLogs": "Ondersteuningslogboeken"
|
||||
},
|
||||
"offline": {
|
||||
|
@ -234,10 +234,14 @@
|
||||
"activityLog": "Логи активностей",
|
||||
"backupComplete": "Бекап успішно завершено для сервера {}",
|
||||
"backupStarted": "Бекап успішно розпочато для сервера {}",
|
||||
"backup_desc": "Ми зафіксували, що міграція бекапів можливо частково або повністю провалилась. Будь ласка перевірте ваші записи бекапів у відповідній вкладці.",
|
||||
"backup_title": "Увага міграція бекапів",
|
||||
"downloadLogs": "Завантажити логи для підтримки?",
|
||||
"finishedPreparing": "Ми підготували логи. Будь ласка натисніть завантажити",
|
||||
"logout": "Вихід",
|
||||
"preparingLogs": "Будь ласка зачекайте поки ми підготуємо для вас логи... Ми надішлемо вам сповіщення коли усе буде готово. Це може зайняти трішки часу для великих проєктів.",
|
||||
"schedule_desc": "Ми зафіксували, що деякі або всі ваші заплановані завдання не вдалось успішно перенести поки робиться оновлення. Будь ласка перевірте ваші заплановані завдання у відповідній вкладці.",
|
||||
"schedule_title": "Увага міграція запланованих завдань",
|
||||
"supportLogs": "Логи для підтримки"
|
||||
},
|
||||
"offline": {
|
||||
|
22
main.py
22
main.py
@ -115,6 +115,23 @@ def controller_setup():
|
||||
controller.clear_support_status()
|
||||
|
||||
|
||||
def get_migration_notifications():
|
||||
migration_notifications = []
|
||||
for file in os.listdir(
|
||||
os.path.join(APPLICATION_PATH, "app", "migrations", "status")
|
||||
):
|
||||
if os.path.isfile(file):
|
||||
with open(
|
||||
os.path.join(APPLICATION_PATH, "app", "migrations", "status", file),
|
||||
encoding="utf-8",
|
||||
) as status_file:
|
||||
status_json = json.load(status_file)
|
||||
for item in status_json:
|
||||
if not status_json[item].get("status"):
|
||||
migration_notifications.append(item)
|
||||
return migration_notifications
|
||||
|
||||
|
||||
def tasks_starter():
|
||||
"""
|
||||
Method starts stats recording, app scheduler, and
|
||||
@ -350,6 +367,9 @@ if __name__ == "__main__":
|
||||
helper.db_path, pragmas={"journal_mode": "wal", "cache_size": -1024 * 10}
|
||||
)
|
||||
database_proxy.initialize(database)
|
||||
Helpers.ensure_dir_exists(
|
||||
os.path.join(APPLICATION_PATH, "app", "migrations", "status")
|
||||
)
|
||||
migration_manager = MigrationManager(database, helper)
|
||||
migration_manager.up() # Automatically runs migrations
|
||||
|
||||
@ -408,7 +428,7 @@ if __name__ == "__main__":
|
||||
controller.set_project_root(APPLICATION_PATH)
|
||||
tasks_manager = TasksManager(helper, controller, file_helper)
|
||||
import3 = Import3(helper, controller)
|
||||
|
||||
helper.migration_notifications = get_migration_notifications()
|
||||
# Check to see if client config.json version is different than the
|
||||
# Master config.json in helpers.py
|
||||
Console.info("Checking for remote changes to config.json")
|
||||
|
@ -4,13 +4,13 @@ argon2-cffi==23.1.0
|
||||
cached_property==1.5.2
|
||||
colorama==0.4.6
|
||||
croniter==1.4.1
|
||||
cryptography==42.0.4
|
||||
cryptography==43.0.1
|
||||
libgravatar==1.0.4
|
||||
nh3==0.2.14
|
||||
packaging==23.2
|
||||
peewee==3.13
|
||||
psutil==5.9.5
|
||||
pyOpenSSL==24.0.0
|
||||
pyOpenSSL==24.2.1
|
||||
pyjwt==2.8.0
|
||||
PyYAML==6.0.1
|
||||
requests==2.32.0
|
||||
|
@ -3,7 +3,7 @@ sonar.organization=crafty-controller
|
||||
|
||||
# This is the name and version displayed in the SonarCloud UI.
|
||||
sonar.projectName=Crafty 4
|
||||
sonar.projectVersion=4.4.4
|
||||
sonar.projectVersion=4.4.5
|
||||
sonar.python.version=3.9, 3.10, 3.11
|
||||
sonar.exclusions=app/migrations/**, app/frontend/static/assets/vendors/**
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user