mirror of
https://github.com/hubHarmony/servii-backend.git
synced 2024-11-17 21:40:31 +00:00
[+] First commit
This commit is contained in:
commit
a08cb54e06
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
/server/
|
||||
/users/
|
||||
unit_test.py
|
||||
!*.py
|
BIN
__pycache__/file_manager.cpython-312.pyc
Normal file
BIN
__pycache__/file_manager.cpython-312.pyc
Normal file
Binary file not shown.
BIN
__pycache__/generic_executor.cpython-312.pyc
Normal file
BIN
__pycache__/generic_executor.cpython-312.pyc
Normal file
Binary file not shown.
BIN
__pycache__/server_mc_manager.cpython-312.pyc
Normal file
BIN
__pycache__/server_mc_manager.cpython-312.pyc
Normal file
Binary file not shown.
119
api.py
Normal file
119
api.py
Normal file
@ -0,0 +1,119 @@
|
||||
from flask import Flask, current_app, make_response, request, jsonify, Response
|
||||
from flask_cors import CORS
|
||||
from typing import Dict
|
||||
import generic_executor
|
||||
import json
|
||||
|
||||
app = Flask(__name__)
|
||||
CORS(app)
|
||||
cors = CORS(app, origins=['*'])
|
||||
|
||||
@app.route('/AccountCreate', methods=['POST'])
|
||||
def account_create() -> Response:
|
||||
data: Dict[str, str] = request.get_json()
|
||||
if not data:
|
||||
return jsonify({"error": "No JSON payload"}), 415
|
||||
|
||||
email: str = data.get('email')
|
||||
port: str = data.get('port')
|
||||
print(f'Email received: {email}')
|
||||
print(f'Port received: {port}')
|
||||
generic_executor.AccountCreate(port)
|
||||
return jsonify({'message': 'OK'}), 200
|
||||
|
||||
@app.route('/ServerCreate', methods=['POST'])
|
||||
def server_create() -> Response:
|
||||
data: Dict[str, str] = request.get_json()
|
||||
if not data:
|
||||
return jsonify({"error": "No JSON payload"}), 415
|
||||
|
||||
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}')
|
||||
generic_executor.ServerCreate(port, name, version)
|
||||
return jsonify({'message': 'OK'}), 200
|
||||
|
||||
@app.route('/ServerDelete', methods=['POST'])
|
||||
def server_delete() -> Response:
|
||||
data: Dict[str, str] = request.get_json()
|
||||
if not data:
|
||||
return jsonify({"error": "No JSON payload"}), 415
|
||||
|
||||
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}')
|
||||
generic_executor.ServerDelete(port, name)
|
||||
return jsonify({'message': 'OK'}), 200
|
||||
|
||||
@app.route('/AccountDelete', methods=['POST'])
|
||||
def account_delete() -> Response:
|
||||
data: Dict[str, str] = request.get_json()
|
||||
if not data:
|
||||
return jsonify({"error": "No JSON payload"}), 415
|
||||
|
||||
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}')
|
||||
generic_executor.AccountDelete(port)
|
||||
return jsonify({'message': 'OK'}), 200
|
||||
|
||||
@app.route('/ServerRun', methods=['POST'])
|
||||
def server_run() -> Response:
|
||||
data: Dict[str, str] = request.get_json()
|
||||
if not data:
|
||||
return jsonify({"error": "No JSON payload"}), 415
|
||||
|
||||
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}')
|
||||
generic_executor.ServerRun(port, name)
|
||||
return jsonify({'message': 'OK'}), 200
|
||||
|
||||
@app.route('/ServerStop', methods=['POST'])
|
||||
def server_stop() -> Response:
|
||||
data: Dict[str, str] = request.get_json()
|
||||
if not data:
|
||||
return jsonify({"error": "No JSON payload"}), 415
|
||||
|
||||
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}')
|
||||
generic_executor.ServerStop(port, name)
|
||||
return jsonify({'message': 'OK'}), 200
|
||||
|
||||
@app.route('/UpdateProperty', methods=['POST'])
|
||||
def update_property() -> Response:
|
||||
data: Dict[str, str, str, str] = request.get_json()
|
||||
if not data:
|
||||
return jsonify({"error": "No JSON payload"}), 415
|
||||
|
||||
port: str = data.get('port')
|
||||
name: str = data.get('name')
|
||||
prop: str = data.get('property')
|
||||
value: str = data.get('value')
|
||||
print(f'The server {name} at port {port} changed {prop} to {value}')
|
||||
generic_executor.UpdateProperty(port, name, prop, value)
|
||||
return jsonify({'message': 'OK'}), 200
|
||||
|
||||
@app.route('/Command', methods=['POST'])
|
||||
def command() -> Response:
|
||||
data: Dict[str, str, str] = request.get_json()
|
||||
if not data:
|
||||
return jsonify({"error": "No JSON payload"}), 415
|
||||
|
||||
port: str = data.get('port')
|
||||
command: str = data.get('command')
|
||||
print(f'Server {port} executed command {command}')
|
||||
generic_executor.RunCommand(port, command)
|
||||
return jsonify({'message': 'OK'}), 200
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
app.run(host='0.0.0.0', port=3005, debug=True)
|
99
api_sender.html
Normal file
99
api_sender.html
Normal file
@ -0,0 +1,99 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>API Interaction Form</title>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<h2>Generic Calls</h2>
|
||||
<form id="genericForm">
|
||||
Email: <input type="text" id="accountEmail"><br>
|
||||
Port: <input type="number" id="accountPort"><br>
|
||||
Name: <input type="text" id="serverName"><br>
|
||||
Version: <input type="number" id="serverVersion"><br>
|
||||
<button type="button" class="actionButton" data-action="AccountCreate">Create Account</button>
|
||||
<button type="button" class="actionButton" data-action="AccountDelete">Delete Account</button>
|
||||
<button type="button" class="actionButton" data-action="ServerCreate">Create Server</button>
|
||||
<button type="button" class="actionButton" data-action="ServerDelete">Delete Server</button>
|
||||
<button type="button" class="actionButton" data-action="ServerRun">Start Server</button>
|
||||
<button type="button" class="actionButton" data-action="ServerStop">Stop Server</button>
|
||||
</form>
|
||||
|
||||
<h2>Update Property</h2>
|
||||
<form id="updatePropertyForm">
|
||||
Property: <input type="text" id="update_property"><br>
|
||||
Value: <input type="text" id="update_value"><br>
|
||||
<button type="button" class="actionButton" data-action="UpdateProperty">Update Property</button>
|
||||
</form>
|
||||
|
||||
<h2>Send Command</h2>
|
||||
<form id="sendCommandForm">
|
||||
Command: <input type="text" id="command"><br>
|
||||
<button type="button" class="actionButton" data-action="Command">Send command</button>
|
||||
</form>
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const forms = document.querySelectorAll('form');
|
||||
const buttons = document.querySelectorAll('.actionButton');
|
||||
|
||||
buttons.forEach(button => {
|
||||
button.addEventListener('click', async event => {
|
||||
const form = event.target.closest('form');
|
||||
const action = button.dataset.action;
|
||||
const email = document.getElementById('accountEmail').value;
|
||||
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 value = document.getElementById('update_value').value;
|
||||
const command = document.getElementById('command').value;
|
||||
var data = {}
|
||||
switch(action) {
|
||||
case 'AccountCreate':
|
||||
data = {email, port}
|
||||
break;
|
||||
case 'AccountDelete':
|
||||
data = {email, port}
|
||||
break;
|
||||
case 'ServerCreate':
|
||||
data = {port, name, version}
|
||||
break;
|
||||
case 'ServerDelete':
|
||||
data = {port, name}
|
||||
break;
|
||||
case 'ServerRun':
|
||||
data = {port, name}
|
||||
break;
|
||||
case 'ServerStop':
|
||||
data = {port, name}
|
||||
break;
|
||||
case 'UpdateProperty':
|
||||
data = {port, name, property, value}
|
||||
break;
|
||||
case 'Command':
|
||||
data = {port, name, command}
|
||||
break;
|
||||
}
|
||||
sendRequest(action, data)
|
||||
.then(response => response.text())
|
||||
.then(data => alert(`Response: ${data}`))
|
||||
.catch(error => console.error('Error:', error));
|
||||
});
|
||||
});
|
||||
|
||||
function sendRequest(endpoint, payload) {
|
||||
return fetch(`http://localhost:3005/${endpoint}`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify(payload)
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
46
file_manager.py
Normal file
46
file_manager.py
Normal file
@ -0,0 +1,46 @@
|
||||
import os
|
||||
import shutil
|
||||
import re
|
||||
|
||||
def create_folder(path):
|
||||
os.makedirs(path, exist_ok=True)
|
||||
|
||||
def check_if_exists(path):
|
||||
return os.path.exists(path)
|
||||
|
||||
def delete_non_empty_folder(path):
|
||||
shutil.rmtree(path)
|
||||
|
||||
def copy_folder_contents(source_dir, destination_dir):
|
||||
if not os.path.exists(destination_dir):
|
||||
os.makedirs(destination_dir)
|
||||
for item_name in os.listdir(source_dir):
|
||||
source_path = os.path.join(source_dir, item_name)
|
||||
destination_path = os.path.join(destination_dir, item_name)
|
||||
if os.path.isfile(source_path):
|
||||
shutil.copy2(source_path, destination_path)
|
||||
elif os.path.isdir(source_path):
|
||||
if os.path.exists(destination_path):
|
||||
for filename in os.listdir(source_path):
|
||||
file_source_path = os.path.join(source_path, filename)
|
||||
file_destination_path = os.path.join(destination_path, filename)
|
||||
if not os.path.exists(file_destination_path):
|
||||
shutil.copy2(file_source_path, file_destination_path)
|
||||
else:
|
||||
shutil.copytree(source_path, destination_path)
|
||||
|
||||
def update_server_property(file_path, property_name, new_value):
|
||||
pattern = rf'^{property_name}=.*$'
|
||||
with open(file_path, 'r') as file:
|
||||
content = file.readlines()
|
||||
for i, line in enumerate(content):
|
||||
if re.match(pattern, line):
|
||||
content[i] = f"{property_name}={new_value}\n"
|
||||
break
|
||||
else:
|
||||
raise ValueError(f"Property '{property_name}' not found in the file.")
|
||||
with open(file_path, 'w') as file:
|
||||
file.writelines(content)
|
||||
|
||||
if __name__ == "__main__":
|
||||
pass
|
37
generic_executor.py
Normal file
37
generic_executor.py
Normal file
@ -0,0 +1,37 @@
|
||||
from server_mc_manager import MinecraftServerManager
|
||||
import file_manager
|
||||
|
||||
mc_manager: MinecraftServerManager = MinecraftServerManager()
|
||||
|
||||
def AccountCreate(port) -> bool:
|
||||
file_manager.create_folder("users/"+port)
|
||||
|
||||
def ServerCreate(port, name, version) -> bool:
|
||||
server_path: str = f"users/{port}/{name}"
|
||||
server_template_path: str = "servers/"+version
|
||||
file_manager.create_folder(server_path)
|
||||
file_manager.copy_folder_contents(server_template_path, server_path)
|
||||
file_manager.update_server_property(server_path+"/server.properties", "server-port", port)
|
||||
|
||||
def ServerDelete(port, name) -> bool:
|
||||
server_path: str = f"users/{port}/{name}"
|
||||
file_manager.delete_non_empty_folder(server_path)
|
||||
|
||||
def AccountDelete(port) -> bool:
|
||||
file_manager.delete_non_empty_folder("users/"+port)
|
||||
|
||||
def ServerRun(port, name) -> bool:
|
||||
server_id = mc_manager.start_server(f"users/{port}/{name}")
|
||||
mc_manager.servers[server_id]['port'] = int(port)
|
||||
|
||||
def ServerStop(port, name) -> bool:
|
||||
server_id = mc_manager.get_server_id_by_port(int(port))
|
||||
mc_manager.stop_server(server_id)
|
||||
|
||||
def UpdateProperty(port, name, prop, value) -> bool:
|
||||
property_file_path: str = f"users/{port}/{name}/server.properties"
|
||||
file_manager.update_server_property(property_file_path, prop, value)
|
||||
|
||||
def RunCommand(port, command) -> bool:
|
||||
server_id = mc_manager.get_server_id_by_port(int(port))
|
||||
mc_manager.execute_server_command(server_id, command)
|
60
server_mc_manager.py
Normal file
60
server_mc_manager.py
Normal file
@ -0,0 +1,60 @@
|
||||
import subprocess
|
||||
import shlex
|
||||
|
||||
class MinecraftServerManager:
|
||||
def __init__(self):
|
||||
self.servers = {}
|
||||
|
||||
def start_server(self, server_directory, java_executable='java', jar_file='server.jar', memory_size='2048M'):
|
||||
command = f"{java_executable} -Xmx{memory_size} -Xms{memory_size} -jar {jar_file} > /dev/null"
|
||||
process = subprocess.Popen(shlex.split(command), cwd=server_directory, stdin=subprocess.PIPE)
|
||||
|
||||
server_id = len(self.servers) + 1
|
||||
self.servers[server_id] = {
|
||||
'process': process,
|
||||
'directory': server_directory,
|
||||
'port': None
|
||||
}
|
||||
|
||||
print(f"Started server {server_id} in directory {server_directory}")
|
||||
return server_id
|
||||
|
||||
def execute_server_command(self, server_id, command):
|
||||
if server_id in self.servers:
|
||||
process = self.servers[server_id]['process']
|
||||
#process.communicate(input=(command + "\n").encode())
|
||||
process.stdin.write(command.encode() + b'\n')
|
||||
print(f"Server {server_id} executed command : {command}")
|
||||
else:
|
||||
print(f"No server found with ID {server_id}")
|
||||
|
||||
def stop_server(self, server_id):
|
||||
if server_id in self.servers:
|
||||
process = self.servers[server_id]['process']
|
||||
process.communicate(input=b"stop\n")
|
||||
del self.servers[server_id]
|
||||
print(f"Stopped server {server_id}")
|
||||
else:
|
||||
print(f"No server found with ID {server_id}")
|
||||
|
||||
def stop_server_focefully(self, server_id):
|
||||
if server_id in self.servers:
|
||||
process = self.servers[server_id]['process']
|
||||
process.terminate()
|
||||
del self.servers[server_id]
|
||||
print(f"Stopped server {server_id}")
|
||||
else:
|
||||
print(f"No server found with ID {server_id}")
|
||||
|
||||
def get_servers(self):
|
||||
return self.servers.values()
|
||||
|
||||
def get_server_id_by_port(self, port):
|
||||
for server_id, server_info in self.servers.items():
|
||||
if server_info['port'] == port:
|
||||
return server_id
|
||||
return None
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
pass
|
36
unit_test.py
Normal file
36
unit_test.py
Normal file
@ -0,0 +1,36 @@
|
||||
import re
|
||||
|
||||
|
||||
def update_server_property(file_path, property_name, new_value):
|
||||
"""
|
||||
Updates a specified property in a Minecraft server configuration file.
|
||||
|
||||
Parameters:
|
||||
- file_path: Path to the Minecraft server configuration file.
|
||||
- property_name: Name of the property to update.
|
||||
- new_value: New value to set for the property.
|
||||
"""
|
||||
# Regular expression pattern to match lines containing the property name
|
||||
pattern = rf'^{property_name}=.*$'
|
||||
|
||||
# Read the file content
|
||||
with open(file_path, 'r') as file:
|
||||
content = file.readlines()
|
||||
|
||||
# Find the line containing the property and update its value
|
||||
for i, line in enumerate(content):
|
||||
if re.match(pattern, line):
|
||||
content[i] = f"{property_name}={new_value}\n"
|
||||
break
|
||||
else:
|
||||
raise ValueError(f"Property '{property_name}' not found in the file.")
|
||||
|
||||
# Write the updated content back to the file
|
||||
with open(file_path, 'w') as file:
|
||||
file.writelines(content)
|
||||
|
||||
# Example usage
|
||||
file_path = 'folder/server.properties'
|
||||
property_name = 'server-ip'
|
||||
new_value = '127.0.0.1'
|
||||
update_server_property(file_path, property_name, new_value)
|
Loading…
Reference in New Issue
Block a user