diff --git a/.gitlab/docker-build.yml b/.gitlab/docker-build.yml
index 268199fb..297c2b20 100644
--- a/.gitlab/docker-build.yml
+++ b/.gitlab/docker-build.yml
@@ -92,15 +92,15 @@ docker-build-prod:
- docker context create tls-environment
- docker buildx create --name zedBuilder --use tls-environment
- docker buildx build
- --cache-from type=registry,ref="$CI_REGISTRY_IMAGE"
+ --cache-from type=registry,ref="$CI_REGISTRY_IMAGE:latest"
--build-arg BUILDKIT_INLINE_CACHE=1
--build-arg "BUILD_DATE=$(date +"%Y-%m-%dT%H:%M:%SZ")"
--build-arg "BUILD_REF=${CI_COMMIT_SHA}"
--build-arg "CRAFTY_VER=${VERSION}"
- --tag "$CI_REGISTRY_IMAGE"
- --platform linux/arm64/v8,linux/amd64 .
- - docker image tag "$CI_REGISTRY_IMAGE" "$CI_REGISTRY_IMAGE:${VERSION}"
- - docker image push -a
+ --tag "$CI_REGISTRY_IMAGE:$VERSION"
+ --tag "$CI_REGISTRY_IMAGE:latest"
+ --platform linux/arm64/v8,linux/amd64
+ --push .
after_script:
- |
docker buildx rm zedBuilder && echo "Successfully Stopped builder instance" || echo "Failed to stop builder instance."
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 5b3393d0..3f81a9f1 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,18 +1,32 @@
# Changelog
-## --- [4.0.16] - 2022/10/09
+## --- [4.0.17] - 2022/TBD
### New features
TBD
### Bug fixes
+TBD
+### Tweaks
+TBD
+### Lang
+TBD
+
+
+## --- [4.0.16] - 2022/10/23
+### New features
+- Automatically set update url for (new) server creation ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/487))
+- Add filter to logs panel ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/484))
+### Bug fixes
- Fix conditional issue with zip imports/uploads ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/476))
- Fix API Schedule updates ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/478))
- Add port constraint for all server creation & api ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/479))
- Clean up backup configs when deleting servers ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/480))
- Add timeout to socket for servers with incorrect port selection ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/482))
+- Fix server_stats db file when deleting server ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/486))
+- Fix "cannot render after finish" from backup_now ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/489))
+- Fix Support Logs on windows by changing the way we declare projects working directory ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/491) | [Commit](https://gitlab.com/crafty-controller/crafty-4/-/commit/a6aa0f86797856a09c639317c5151c354f4024dc))
### Tweaks
- Fix sidebar to not move when scrolling ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/481))
- Add the rest of CSS predefined colors to themes ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/477))
-### Lang
-TBD
+- Only send realtime stats when clients connected ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/488))
## --- [4.0.15] - 2022/10/02
diff --git a/README.md b/README.md
index babfc564..5ae83d5f 100644
--- a/README.md
+++ b/README.md
@@ -1,12 +1,5 @@
[![Crafty Logo](app/frontend/static/assets/images/logo_long.svg)](https://craftycontrol.com)
-
-[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
-[![Supported Python Versions](https://shields.io/badge/python-3.8%20%7C%203.9%20%7C%203.10%20-blue)](https://www.python.org)
-[![Version](https://img.shields.io/badge/release-v4.0.16-blue)](https://gitlab.com/crafty-controller/crafty-4/-/releases)
-[![Code Quality](https://img.shields.io/badge/code%20quality-10-brightgreen)](https://gitlab.com/crafty-controller/crafty-4)
-[![Build Status](https://gitlab.com/crafty-controller/crafty-4/badges/master/pipeline.svg)](https://gitlab.com/crafty-controller/crafty-4/-/commits/master)
-[![Licence](https://img.shields.io/gitlab/license/20430749)](https://gitlab.com/crafty-controller/crafty-4/-/blob/master/LICENSE)
-# Crafty Controller 4.0.16
+# Crafty Controller 4.0.17
> Python based Control Panel for your Minecraft Server
## What is Crafty Controller?
diff --git a/app/classes/models/server_stats.py b/app/classes/models/server_stats.py
index 29fdb856..ccb21879 100644
--- a/app/classes/models/server_stats.py
+++ b/app/classes/models/server_stats.py
@@ -120,6 +120,7 @@ class HelperServerStats:
return None
def get_all_servers_stats(self):
+ self.database.connect(reuse_if_open=True)
servers = HelperServers.get_all_defined_servers()
server_data = []
try:
@@ -136,18 +137,23 @@ class HelperServerStats:
logger.error(
f"Stats collection failed with error: {ex}. Was a server just created?"
)
+ self.database.close()
return server_data
def get_history_stats(self, server_id, num_days):
+ self.database.connect(reuse_if_open=True)
max_age = datetime.datetime.now() - timedelta(days=num_days)
- return (
+ server_stats = (
ServerStats.select()
.where(ServerStats.created > max_age)
.where(ServerStats.server_id == server_id)
.execute(self.database)
)
+ self.database.connect(reuse_if_open=True)
+ return server_stats
def insert_server_stats(self, server_stats):
+ self.database.connect(reuse_if_open=True)
server_id = server_stats.get("id", 0)
if server_id == 0:
@@ -176,13 +182,18 @@ class HelperServerStats:
}
).execute(self.database)
+ self.database.close()
+
def remove_old_stats(self, last_week):
+ self.database.connect(reuse_if_open=True)
# self.select_database(self.server_id)
ServerStats.delete().where(ServerStats.created < last_week).execute(
self.database
)
+ self.database.close()
def get_latest_server_stats(self):
+ self.database.connect(reuse_if_open=True)
latest = (
ServerStats.select()
.where(ServerStats.server_id == self.server_id)
@@ -190,12 +201,15 @@ class HelperServerStats:
.limit(1)
.get(self.database)
)
+
+ self.database.close()
try:
return DatabaseShortcuts.get_data_obj(latest)
except IndexError:
return {}
def get_server_stats(self):
+ self.database.connect(reuse_if_open=True)
stats = (
ServerStats.select()
.where(ServerStats.server_id == self.server_id)
@@ -203,63 +217,70 @@ class HelperServerStats:
.limit(1)
.first(self.database)
)
+ self.database.close()
return DatabaseShortcuts.get_data_obj(stats)
def server_id_exists(self):
- # self.select_database(self.server_id)
if not HelperServers.get_server_data_by_id(self.server_id):
return False
return True
def sever_crashed(self):
- # self.select_database(self.server_id)
+ self.database.connect(reuse_if_open=True)
ServerStats.update(crashed=True).where(
ServerStats.server_id == self.server_id
).execute(self.database)
+ self.database.close()
def set_import(self):
- # self.select_database(self.server_id)
+ self.database.connect(reuse_if_open=True)
ServerStats.update(importing=True).where(
ServerStats.server_id == self.server_id
).execute(self.database)
+ self.database.close()
def finish_import(self):
- # self.select_database(self.server_id)
+ self.database.connect(reuse_if_open=True)
ServerStats.update(importing=False).where(
ServerStats.server_id == self.server_id
).execute(self.database)
+ self.database.close()
def get_import_status(self):
- # self.select_database(self.server_id)
+ self.database.connect(reuse_if_open=True)
import_status = (
ServerStats.select()
.where(ServerStats.server_id == self.server_id)
.get(self.database)
)
+ self.database.close()
return import_status.importing
def server_crash_reset(self):
if self.server_id is None:
return
- # self.select_database(self.server_id)
+ self.database.connect(reuse_if_open=True)
ServerStats.update(crashed=False).where(
ServerStats.server_id == self.server_id
).execute(self.database)
+ self.database.close()
def is_crashed(self):
- # self.select_database(self.server_id)
+ self.database.connect(reuse_if_open=True)
svr: ServerStats = (
ServerStats.select()
.where(ServerStats.server_id == self.server_id)
.get(self.database)
)
+ self.database.close()
return svr.crashed
def set_update(self, value):
if self.server_id is None:
return
- # self.select_database(self.server_id)
+
+ self.database.connect(reuse_if_open=True)
try:
# Checks if server even exists
ServerStats.select().where(ServerStats.server_id == self.server_id).execute(
@@ -267,22 +288,26 @@ class HelperServerStats:
)
except DoesNotExist as ex:
logger.error(f"Database entry not found! {ex}")
+ self.database.close()
return
+
ServerStats.update(updating=value).where(
ServerStats.server_id == self.server_id
).execute(self.database)
+ self.database.close()
def get_update_status(self):
- # self.select_database(self.server_id)
+ self.database.connect(reuse_if_open=True)
update_status = (
ServerStats.select()
.where(ServerStats.server_id == self.server_id)
.get(self.database)
)
+ self.database.close()
return update_status.updating
def set_first_run(self):
- # self.select_database(self.server_id)
+ self.database.connect(reuse_if_open=True)
# Sets first run to false
try:
# Checks if server even exists
@@ -291,22 +316,25 @@ class HelperServerStats:
)
except Exception as ex:
logger.error(f"Database entry not found! {ex}")
+ self.database.close()
return
ServerStats.update(first_run=False).where(
ServerStats.server_id == self.server_id
).execute(self.database)
+ self.database.close()
def get_first_run(self):
- # self.select_database(self.server_id)
+ self.database.connect(reuse_if_open=True)
first_run = (
ServerStats.select()
.where(ServerStats.server_id == self.server_id)
.get(self.database)
)
+ self.database.close()
return first_run.first_run
def get_ttl_without_player(self):
- # self.select_database(self.server_id)
+ self.database.connect(reuse_if_open=True)
last_stat = (
ServerStats.select()
.where(ServerStats.server_id == self.server_id)
@@ -320,6 +348,7 @@ class HelperServerStats:
.order_by(ServerStats.created.desc())
.first(self.database)
)
+ self.database.close()
return last_stat.created - last_stat_with_player.created
def can_stop_no_players(self, time_limit):
@@ -327,7 +356,7 @@ class HelperServerStats:
return (time_limit == -1) or (ttl_no_players > time_limit)
def set_waiting_start(self, value):
- # self.select_database(self.server_id)
+ self.database.connect(reuse_if_open=True)
try:
# Checks if server even exists
ServerStats.select().where(ServerStats.server_id == self.server_id).execute(
@@ -335,15 +364,19 @@ class HelperServerStats:
)
except DoesNotExist as ex:
logger.error(f"Database entry not found! {ex}")
+ self.database.close()
return
ServerStats.update(waiting_start=value).where(
ServerStats.server_id == self.server_id
).execute(self.database)
+ self.database.close()
def get_waiting_start(self):
+ self.database.connect(reuse_if_open=True)
waiting_start = (
ServerStats.select()
.where(ServerStats.server_id == self.server_id)
.get(self.database)
)
+ self.database.close()
return waiting_start.waiting_start
diff --git a/app/classes/shared/main_controller.py b/app/classes/shared/main_controller.py
index de112f80..e09ff2ec 100644
--- a/app/classes/shared/main_controller.py
+++ b/app/classes/shared/main_controller.py
@@ -472,6 +472,15 @@ class Controller:
data["create_type"] == "minecraft_java"
and root_create_data["create_type"] == "download_jar"
):
+ # modded update urls from server jars will only update the installer
+ if create_data["category"] != "modded":
+ server_obj = self.servers.get_server_obj(new_server_id)
+ url = (
+ f"https://serverjars.com/api/fetchJar/{create_data['category']}"
+ f"/{create_data['type']}/{create_data['version']}"
+ )
+ server_obj.executable_update_url = url
+ self.servers.update_server(server_obj)
self.server_jars.download_jar(
create_data["category"],
create_data["type"],
@@ -555,7 +564,12 @@ class Controller:
user_id,
server_type="minecraft-java",
)
-
+ # modded update urls from server jars will only update the installer
+ if jar != "modded":
+ server_obj = self.servers.get_server_obj(new_id)
+ url = f"https://serverjars.com/api/fetchJar/{jar}/{server}/{version}"
+ server_obj.executable_update_url = url
+ self.servers.update_server(server_obj)
# download the jar
self.server_jars.download_jar(
jar, server, version, os.path.join(server_dir, server_file), new_id
diff --git a/app/classes/shared/server.py b/app/classes/shared/server.py
index 9bc86017..257bf64f 100644
--- a/app/classes/shared/server.py
+++ b/app/classes/shared/server.py
@@ -1223,41 +1223,19 @@ class ServerInstance:
# **********************************************************************************
def realtime_stats(self):
- total_players = 0
- max_players = 0
- servers_ping = []
- raw_ping_result = []
- raw_ping_result = self.get_raw_server_stats(self.server_id)
-
- if f"{raw_ping_result.get('icon')}" == "b''":
- raw_ping_result["icon"] = False
-
- servers_ping.append(
- {
- "id": raw_ping_result.get("id"),
- "started": raw_ping_result.get("started"),
- "running": raw_ping_result.get("running"),
- "cpu": raw_ping_result.get("cpu"),
- "mem": raw_ping_result.get("mem"),
- "mem_percent": raw_ping_result.get("mem_percent"),
- "world_name": raw_ping_result.get("world_name"),
- "world_size": raw_ping_result.get("world_size"),
- "server_port": raw_ping_result.get("server_port"),
- "int_ping_results": raw_ping_result.get("int_ping_results"),
- "online": raw_ping_result.get("online"),
- "max": raw_ping_result.get("max"),
- "players": raw_ping_result.get("players"),
- "desc": raw_ping_result.get("desc"),
- "version": raw_ping_result.get("version"),
- "icon": raw_ping_result.get("icon"),
- "crashed": self.is_crashed,
- }
- )
+ # only get stats if clients are connected.
+ # no point in burning cpu
if len(self.helper.websocket_helper.clients) > 0:
- self.helper.websocket_helper.broadcast_page_params(
- "/panel/server_detail",
- {"id": str(self.server_id)},
- "update_server_details",
+ total_players = 0
+ max_players = 0
+ servers_ping = []
+ raw_ping_result = []
+ raw_ping_result = self.get_raw_server_stats(self.server_id)
+
+ if f"{raw_ping_result.get('icon')}" == "b''":
+ raw_ping_result["icon"] = False
+
+ servers_ping.append(
{
"id": raw_ping_result.get("id"),
"started": raw_ping_result.get("started"),
@@ -1276,24 +1254,53 @@ class ServerInstance:
"version": raw_ping_result.get("version"),
"icon": raw_ping_result.get("icon"),
"crashed": self.is_crashed,
- "created": datetime.datetime.now().strftime("%Y/%m/%d, %H:%M:%S"),
- },
+ }
)
- total_players += int(raw_ping_result.get("online"))
- max_players += int(raw_ping_result.get("max"))
-
- self.record_server_stats()
-
- if (len(servers_ping) > 0) & (len(self.helper.websocket_helper.clients) > 0):
- try:
- self.helper.websocket_helper.broadcast_page(
- "/panel/dashboard", "update_server_status", servers_ping
+ if len(self.helper.websocket_helper.clients) > 0:
+ self.helper.websocket_helper.broadcast_page_params(
+ "/panel/server_detail",
+ {"id": str(self.server_id)},
+ "update_server_details",
+ {
+ "id": raw_ping_result.get("id"),
+ "started": raw_ping_result.get("started"),
+ "running": raw_ping_result.get("running"),
+ "cpu": raw_ping_result.get("cpu"),
+ "mem": raw_ping_result.get("mem"),
+ "mem_percent": raw_ping_result.get("mem_percent"),
+ "world_name": raw_ping_result.get("world_name"),
+ "world_size": raw_ping_result.get("world_size"),
+ "server_port": raw_ping_result.get("server_port"),
+ "int_ping_results": raw_ping_result.get("int_ping_results"),
+ "online": raw_ping_result.get("online"),
+ "max": raw_ping_result.get("max"),
+ "players": raw_ping_result.get("players"),
+ "desc": raw_ping_result.get("desc"),
+ "version": raw_ping_result.get("version"),
+ "icon": raw_ping_result.get("icon"),
+ "crashed": self.is_crashed,
+ "created": datetime.datetime.now().strftime(
+ "%Y/%m/%d, %H:%M:%S"
+ ),
+ },
)
- self.helper.websocket_helper.broadcast_page(
- "/status", "update_server_status", servers_ping
- )
- except:
- Console.critical("Can't broadcast server status to websocket")
+ total_players += int(raw_ping_result.get("online"))
+ max_players += int(raw_ping_result.get("max"))
+
+ self.record_server_stats()
+
+ if (len(servers_ping) > 0) & (
+ len(self.helper.websocket_helper.clients) > 0
+ ):
+ try:
+ self.helper.websocket_helper.broadcast_page(
+ "/panel/dashboard", "update_server_status", servers_ping
+ )
+ self.helper.websocket_helper.broadcast_page(
+ "/status", "update_server_status", servers_ping
+ )
+ except:
+ Console.critical("Can't broadcast server status to websocket")
def get_servers_stats(self):
diff --git a/app/classes/web/ajax_handler.py b/app/classes/web/ajax_handler.py
index c0c49b9a..a90b4141 100644
--- a/app/classes/web/ajax_handler.py
+++ b/app/classes/web/ajax_handler.py
@@ -76,7 +76,7 @@ class AjaxHandler(BaseHandler):
line = re.sub("(\033\\[(0;)?[0-9]*[A-z]?(;[0-9])?m?)", "", line)
line = re.sub("[A-z]{2}\b\b", "", line)
line = self.helper.log_colors(html.escape(line))
- self.write(f"{line}
")
+ self.write(f"{line}
")
# self.write(d.encode("utf-8"))
except Exception as e:
diff --git a/app/config/version.json b/app/config/version.json
index 2dece081..f97d9f2b 100644
--- a/app/config/version.json
+++ b/app/config/version.json
@@ -1,5 +1,5 @@
{
"major": 4,
"minor": 0,
- "sub": 16
+ "sub": 17
}
diff --git a/app/frontend/templates/panel/server_backup.html b/app/frontend/templates/panel/server_backup.html
index 72c37576..a50b55b5 100644
--- a/app/frontend/templates/panel/server_backup.html
+++ b/app/frontend/templates/panel/server_backup.html
@@ -294,9 +294,10 @@
headers: { 'X-XSRFToken': token },
url: '/ajax/backup_now?id=' + server_id,
success: function (data) {
- location.reload();
+ return;
},
});
+ return;
}
function del_backup(filename, id) {
diff --git a/app/frontend/templates/panel/server_logs.html b/app/frontend/templates/panel/server_logs.html
index 8ab272b3..ccf40937 100644
--- a/app/frontend/templates/panel/server_logs.html
+++ b/app/frontend/templates/panel/server_logs.html
@@ -42,10 +42,17 @@