From 8f9592d97aca89841f3d343bc95465779631879c Mon Sep 17 00:00:00 2001 From: Charles Le Maux Date: Mon, 24 Jun 2024 01:50:08 +0100 Subject: [PATCH 1/2] [~] Route handling update ! Signed-off-by: Charles Le Maux --- api.py | 155 ++++++++++--------------------------------- api_sender.html | 4 +- server_mc_manager.py | 3 +- 3 files changed, 40 insertions(+), 122 deletions(-) diff --git a/api.py b/api.py index dcae1dd..0bf0c80 100644 --- a/api.py +++ b/api.py @@ -1,6 +1,5 @@ import http -from enum import Enum -from typing import Dict, Optional +import inspect from flask import Flask, Response, jsonify, request from flask_cors import CORS @@ -12,12 +11,9 @@ CORS(app) cors = CORS(app, origins=['*']) -class ActionType(Enum): - ACCOUNT_CREATE = '/AccountCreate' - SERVER_CREATE = '/ServerCreate' - - -def generic_response_maker(status_code: http.HTTPStatus) -> tuple[Response, int]: +def generic_response_maker(status_code: http.HTTPStatus, _message: str = None) -> tuple[Response, int]: + if _message is not None: + return jsonify({'message': _message}), status_code.value match status_code: case http.HTTPStatus.CREATED: message = jsonify({'message': 'Creation successful.'}) @@ -35,133 +31,54 @@ def generic_response_maker(status_code: http.HTTPStatus) -> tuple[Response, int] message = jsonify({'message': 'Unsupported Media Type / No JSON payload'}) case http.HTTPStatus.OK: message = jsonify({'message': 'Success.'}) + case http.HTTPStatus.METHOD_NOT_ALLOWED: + message = jsonify({'message': 'This API call does not exist.'}) case _: status_code = http.HTTPStatus.BAD_GATEWAY message = jsonify({'message': 'Bad Gateway.'}) return message, status_code.value -def parse_and_validate_request(required_keys: set) -> Optional[dict]: +def parse_and_validate_request(parameters: [str]) -> list[str]: + args = [] data = request.get_json() if not data: raise Exception("Empty request body.") - missing_keys = required_keys - set(data.keys()) - if missing_keys: - raise Exception("Missing keys: {}".format(missing_keys)) - return data + for name in parameters: + if name not in data: + raise Exception(f"Missing parameter {name}") + value = data[name] + if isinstance(value, str): + args.append(value) + return args -@app.route('/AccountCreate', methods=['POST']) -def account_create() -> tuple[Response, int]: - data: Dict[str, str] = request.get_json() - if not data: - return generic_response_maker(http.HTTPStatus.UNSUPPORTED_MEDIA_TYPE) - - email: str = data.get('email') - port: str = data.get('port') - print(f'Email received: {email}') - print(f'Port received: {port}') - status: http.HTTPStatus = generic_executor.account_create(port) - return generic_response_maker(status) +route_handlers = { + 'AccountCreate': generic_executor.account_create, + 'ServerCreate': generic_executor.server_create, + 'ServerDelete': generic_executor.server_delete, + 'AccountDelete': generic_executor.account_delete, + 'ServerRun': generic_executor.server_run, + 'ServerStop': generic_executor.server_stop, + 'UpdateProperty': generic_executor.update_property, + 'Command': generic_executor.run_command, +} -@app.route('/ServerCreate', methods=['POST']) -def server_create() -> tuple[Response, int]: - data: Dict[str, str] = request.get_json() - if not data: - return generic_response_maker(http.HTTPStatus.UNSUPPORTED_MEDIA_TYPE) +@app.route('/', methods=['POST']) +def dynamic_route_handler(path): + if path not in route_handlers: + return generic_response_maker(http.HTTPStatus.METHOD_NOT_ALLOWED) - port: str = data.get('port') - name: str = data.get('name') - version: str = data.get('version') - print(f'Port received for server creation: {port}') - print(f'Server name: {name}') - print(f'Server version: {version}') - status: http.HTTPStatus = generic_executor.server_create(port, name, version) - return generic_response_maker(status) + route_fn = route_handlers[path] + parameters = [] + sig = inspect.signature(route_fn) + for param in sig.parameters.values(): + parameters.append(param.name) -@app.route('/ServerDelete', methods=['POST']) -def server_delete() -> tuple[Response, int]: - data: Dict[str, str] = request.get_json() - if not data: - return generic_response_maker(http.HTTPStatus.UNSUPPORTED_MEDIA_TYPE) - - port: str = data.get('port') - name: str = data.get('name') - print(f'Port received for server deletion: {port}') - print(f'Server name to delete: {name}') - status: http.HTTPStatus = generic_executor.server_delete(port, name) - return generic_response_maker(status) - - -@app.route('/AccountDelete', methods=['POST']) -def account_delete() -> tuple[Response, int]: - data: Dict[str, str] = request.get_json() - if not data: - return generic_response_maker(http.HTTPStatus.UNSUPPORTED_MEDIA_TYPE) - - port: str = data.get('port') - email: str = data.get('email') - print(f'Port received for account deletion: {port}') - print(f'Email of account to delete: {email}') - status: http.HTTPStatus = generic_executor.account_delete(port) - return generic_response_maker(status) - - -@app.route('/ServerRun', methods=['POST']) -def server_run() -> tuple[Response, int]: - data: Dict[str, str] = request.get_json() - if not data: - return generic_response_maker(http.HTTPStatus.UNSUPPORTED_MEDIA_TYPE) - - port: str = data.get('port') - name: str = data.get('name') - print(f'Port received for server launch: {port}') - print(f'Server name to launch: {name}') - status: http.HTTPStatus = generic_executor.server_run(port, name) - return generic_response_maker(status) - - -@app.route('/ServerStop', methods=['POST']) -def server_stop() -> tuple[Response, int]: - data: Dict[str, str] = request.get_json() - if not data: - return generic_response_maker(http.HTTPStatus.UNSUPPORTED_MEDIA_TYPE) - - port: str = data.get('port') - name: str = data.get('name') - print(f'Port received for server shutdown: {port}') - print(f'Server name to shut down: {name}') - status: http.HTTPStatus = generic_executor.server_stop(port, name) - return generic_response_maker(status) - - -@app.route('/UpdateProperty', methods=['POST']) -def update_property() -> tuple[Response, int]: - data: Dict[str, str, str, str] = request.get_json() - if not data: - return generic_response_maker(http.HTTPStatus.UNSUPPORTED_MEDIA_TYPE) - - port: str = data.get('port') - name: str = data.get('name') - prop: str = data.get('property') - value: str = data.get('value') - status: http.HTTPStatus = generic_executor.update_property(port, name, prop, value) - print(f'The server {name} at port {port} changed {prop} to {value}') - return generic_response_maker(status) - - -@app.route('/Command', methods=['POST']) -def command() -> tuple[Response, int]: - data: Dict[str, str, str] = request.get_json() - if not data: - return generic_response_maker(http.HTTPStatus.UNSUPPORTED_MEDIA_TYPE) - - port: str = data.get('port') - _command: str = data.get('command') - print(f'Server {port} executed command {_command}') - status: http.HTTPStatus = generic_executor.run_command(port, _command) + mapped_parameters = parse_and_validate_request(parameters) + status: http.HTTPStatus = route_fn(*mapped_parameters) return generic_response_maker(status) diff --git a/api_sender.html b/api_sender.html index af29aae..865bc99 100644 --- a/api_sender.html +++ b/api_sender.html @@ -46,7 +46,7 @@ document.addEventListener('DOMContentLoaded', () => { const port = document.getElementById('accountPort').value; const name = document.getElementById('serverName').value; const version = document.getElementById('serverVersion').value; - const property = document.getElementById('update_property').value; + const prop = document.getElementById('update_property').value; const value = document.getElementById('update_value').value; const command = document.getElementById('command').value; var data = {} @@ -70,7 +70,7 @@ document.addEventListener('DOMContentLoaded', () => { data = {port, name} break; case 'UpdateProperty': - data = {port, name, property, value} + data = {port, name, prop, value} break; case 'Command': data = {port, name, command} diff --git a/server_mc_manager.py b/server_mc_manager.py index b194250..4dc1bee 100644 --- a/server_mc_manager.py +++ b/server_mc_manager.py @@ -23,7 +23,8 @@ class MinecraftServerManager: def execute_server_command(self, server_id, command): if server_id in self.servers: process = self.servers[server_id]['process'] - process.stdin.write(command.encode() + b'\n') + process.stdin.write(command.encode('utf-8') + b'\n') + process.stdin.flush() print(f"Server {server_id} executed command : {command}") else: print(f"No server found with ID {server_id}") From bf98e84fd7e3feaba9a684cfc8a4c41723a8ae99 Mon Sep 17 00:00:00 2001 From: Charles Le Maux Date: Mon, 24 Jun 2024 01:52:23 +0100 Subject: [PATCH 2/2] [~] conventional renaming Signed-off-by: Charles Le Maux --- api_sender.html => api_sender_v1.html | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename api_sender.html => api_sender_v1.html (100%) diff --git a/api_sender.html b/api_sender_v1.html similarity index 100% rename from api_sender.html rename to api_sender_v1.html