From 6600b751ea1b15a160539ff0e5be279c74417a89 Mon Sep 17 00:00:00 2001 From: Charles Le Maux Date: Tue, 20 Aug 2024 20:03:59 +0200 Subject: [PATCH 1/8] [+] Going ahead master --- server_mc_manager.py | 1 + 1 file changed, 1 insertion(+) diff --git a/server_mc_manager.py b/server_mc_manager.py index 138968c..0135661 100644 --- a/server_mc_manager.py +++ b/server_mc_manager.py @@ -32,6 +32,7 @@ class MinecraftServerManager: command = f"{java_executable} -Xmx{memory_size} {reg_flags} -jar {jar_file} --nogui" process = subprocess.Popen(shlex.split(command), cwd=server_directory, stdin=subprocess.PIPE) + #TODO: Track process behavior and stderr, while excepting Advanced Terminal features not to be avail. self.servers_count = len(self.servers) + 1 self.servers[port] = { From 1f8c6c22afafe9a13030a34f33f24bf4991f77a7 Mon Sep 17 00:00:00 2001 From: Charles Le Maux Date: Tue, 20 Aug 2024 20:07:39 +0200 Subject: [PATCH 2/8] [+] Fixed non-static get_online_players --- server_mc_manager.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/server_mc_manager.py b/server_mc_manager.py index 0135661..df94b76 100644 --- a/server_mc_manager.py +++ b/server_mc_manager.py @@ -76,6 +76,8 @@ class MinecraftServerManager: return None def get_online_players(self, port) -> int: + if not self.servers[port]: + return 0 with mcipc.query.Client('127.0.0.1', port) as client: stats: mcipc.query.proto.FullStats = client.stats(full=True) stats: int = stats.num_players From 79d680db3a03263b28159e1b2e6a2ac61a853abb Mon Sep 17 00:00:00 2001 From: Charles Le Maux Date: Sat, 24 Aug 2024 12:44:35 +0200 Subject: [PATCH 3/8] [+] Fixed wrong int type in server prop on server creation. --- firebase_manager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/firebase_manager.py b/firebase_manager.py index efe0a5a..110324d 100644 --- a/firebase_manager.py +++ b/firebase_manager.py @@ -126,7 +126,7 @@ def create_server(user_id: str, server_name: str, version: str, port: int, frame "motd": "A Minecraft Server", "pvp": "true", "onlineMode": "true", - "maxPlayers": 20, + "maxPlayers": "20", "enableCommandBlock": "false"}) From 2632f70b652542c801565a29cd163e3633c4e925 Mon Sep 17 00:00:00 2001 From: Charles Le Maux Date: Sat, 24 Aug 2024 13:23:19 +0200 Subject: [PATCH 4/8] [+] FetchPlayersStatus API call. Users are now able to fetch their user data : Whitelist, Operators, BannedIps, BannedPlayers. --- app.py | 1 + generic_executor.py | 32 +++++++++++++++++++++++++++++++- 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/app.py b/app.py index 0245a7b..0d408c7 100644 --- a/app.py +++ b/app.py @@ -82,6 +82,7 @@ route_handlers = { 'FetchServers': generic_executor.fetch_servers, 'FetchLogs': generic_executor.fetch_logs, 'FetchHistory': generic_executor.fetch_history, + 'FetchPlayersStatus': generic_executor.fetch_players_status, 'AccountCreate': generic_executor.account_create, 'ServerCreate': generic_executor.server_create, 'ServerDelete': generic_executor.server_delete, diff --git a/generic_executor.py b/generic_executor.py index 57774aa..c3b64a5 100644 --- a/generic_executor.py +++ b/generic_executor.py @@ -1,5 +1,7 @@ +import json +import os from http import HTTPStatus -from typing import Union +from typing import Union, Tuple, Dict from cloudflare.types.dns import SRVRecord from firebase_admin.auth import UserRecord @@ -95,6 +97,34 @@ def fetch_history(user: UserRecord, name: str) -> tuple[HTTPStatus, Union[str, N return HTTPStatus.INTERNAL_SERVER_ERROR, "Unknown error." +def fetch_players_status(user: UserRecord, name: str) -> tuple[HTTPStatus, Union[dict[str, str], str]]: + user_id: str = user.uid + server_path = f"users/{user_id}/{name}" + players_status_files = { + "Whitelist": "whitelist.json", + "BannedPlayers": "banned-players.json", + "BannedIps": "banned-ips.json", + "Operators": "ops.json" + } + try: + file_contents = {} + + for file_name, file_path in players_status_files.items(): + full_file_path = f"{server_path}/{file_path}" + if os.path.exists(full_file_path): + with open(full_file_path, 'r') as file: + file_contents[file_name] = file.read() + else: + return HTTPStatus.NOT_FOUND, "Launch the server at least once." + + file_contents_json = json.dumps(file_contents, indent=4) + + return HTTPStatus.OK, file_contents + except Exception as e: + file_manager.log_error(type(e).__name__, str(e)) + return HTTPStatus.INTERNAL_SERVER_ERROR, "Unknown error." + + def account_create(user: UserRecord) -> tuple[HTTPStatus, Union[str, None]]: if firebase_manager.user_field_exists(user.uid): return HTTPStatus.FORBIDDEN, "User already exists." From c60fab04c1a587e78782c40e431a12c7dff43bbc Mon Sep 17 00:00:00 2001 From: Charles Le Maux Date: Sat, 24 Aug 2024 13:23:23 +0200 Subject: [PATCH 5/8] [+] FetchPlayersStatus API call. Users are now able to fetch their user data : Whitelist, Operators, BannedIps, BannedPlayers. --- api_sender.html | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/api_sender.html b/api_sender.html index 8b028cb..a24a7bc 100644 --- a/api_sender.html +++ b/api_sender.html @@ -21,6 +21,7 @@ + @@ -50,7 +51,7 @@ document.addEventListener('DOMContentLoaded', () => { buttons.forEach(button => { button.addEventListener('click', async event => { const action = button.dataset.action; - const token = "MpkbDMOO8PQddQgB5VgBQdTMWF53"; + const token = "kPbH7QbrOzTrNcpqTnZGDJfSj3E3"; const framework = document.getElementById('serverFramework').value; const subdomain = document.getElementById('subdomain').value; const email = document.getElementById('accountEmail').value; @@ -69,6 +70,9 @@ document.addEventListener('DOMContentLoaded', () => { case 'FetchLogs': data = {token, name}; break; + case 'FetchPlayersStatus': + data = {token, name}; + break; case 'AccountCreate': data = {email, port, token}; break; @@ -105,7 +109,7 @@ document.addEventListener('DOMContentLoaded', () => { }); function sendRequest(endpoint, payload) { - return fetch(`http://127.0.0.1:3000/${endpoint}`, { + return fetch(`http://localhost:3000/${endpoint}`, { method: 'POST', headers: { 'Content-Type': 'application/json' From 0d0c655d7f9f59e5ce3bb6c6c24f3d83c0cfe71d Mon Sep 17 00:00:00 2001 From: Charles Le Maux Date: Sat, 24 Aug 2024 13:30:25 +0200 Subject: [PATCH 6/8] [+] UpdateProperties call now updates the modified the updated properties in the history.log --- generic_executor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/generic_executor.py b/generic_executor.py index c3b64a5..ff3275b 100644 --- a/generic_executor.py +++ b/generic_executor.py @@ -288,7 +288,7 @@ def update_properties(user: UserRecord, name: str, props: list[tuple[str, str]]) errors.append(message) if len(errors) > 0: return HTTPStatus.IM_A_TEAPOT, str(errors) - file_manager.log_action(user.uid, name, "UpdateProperties") + file_manager.log_action(user.uid, name, "UpdateProperties", str(props)) return HTTPStatus.OK, f"Successfully updated server '{name}'." From 3afec9a63a7b653a366d40382ca0e94f601a9b34 Mon Sep 17 00:00:00 2001 From: Charles Le Maux Date: Sat, 24 Aug 2024 13:35:59 +0200 Subject: [PATCH 7/8] [+] Safe API closing handling attempt --- app.py | 6 ++++++ firebase_manager.py | 16 ++++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/app.py b/app.py index 0d408c7..d801760 100644 --- a/app.py +++ b/app.py @@ -7,6 +7,7 @@ from flask_cors import CORS import firebase_manager import generic_executor +import atexit app = Flask(__name__) cors = CORS(app, origins="*") @@ -116,7 +117,12 @@ def dynamic_route_handler(path): return generic_response_maker(http.HTTPStatus.BAD_REQUEST, str(e)) +def exit_safety() -> None: + firebase_manager.set_servers_not_running() + return + app.register_blueprint(apiBP) if __name__ == '__main__': + atexit.register(exit_safety) app.run(host='0.0.0.0', port=3000, debug=False) diff --git a/firebase_manager.py b/firebase_manager.py index 110324d..2c076ad 100644 --- a/firebase_manager.py +++ b/firebase_manager.py @@ -154,6 +154,22 @@ def update_server_property(user_id: str, server_name: str, prop: str, value: str server_ref.update({prop: value}) +def set_servers_not_running(): + users_ref = firestore_database.collection(u'users') + docs = users_ref.stream() + + for doc in docs: + user_id = doc.id + servers_ref = firestore_database.collection(u'users').document(user_id).collection(u'servers') + server_docs = servers_ref.stream() + + for server_doc in server_docs: + server_id = server_doc.id + firestore_database.collection(u'users').document(user_id).collection(u'servers').document(server_id).update({u'running': False}) + + print("All servers have been set to not running.") + + def log_exception_to_firestore(exception: Exception = None, user_id: str = None, data: dict = None): new_id: str = datetime.now().strftime('%Y-%m-%d %H:%M:%S %Z%z') log_entry = { From b70f522a3bc1a30985d2fddf7752b8a1f624feff Mon Sep 17 00:00:00 2001 From: Charles Le Maux Date: Sat, 24 Aug 2024 13:38:22 +0200 Subject: [PATCH 8/8] [~] Added servers closing to unit test --- unit_test.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/unit_test.py b/unit_test.py index 5554123..11617b7 100644 --- a/unit_test.py +++ b/unit_test.py @@ -12,4 +12,6 @@ def ban_user(user_id: str): if __name__ == '__main__': #ban_user("MpkbDMOO8PQddQgB5VgBQdTMWF53") - file_manager.log_action("gqZN3eCHF3V2er3Py3rlgk8u2t83", "test", "DeleteServer") + #file_manager.log_action("gqZN3eCHF3V2er3Py3rlgk8u2t83", "test", "DeleteServer") + #firebase_manager.set_servers_not_running() + pass