345 lines
10 KiB
Python
345 lines
10 KiB
Python
import json
|
|
import os
|
|
import shutil
|
|
import subprocess
|
|
|
|
import docker
|
|
import requests
|
|
from flask import Blueprint, jsonify, request, send_from_directory, abort
|
|
|
|
from .auth import oidc
|
|
from .docker_utils import start_server, stop_server, restart_server, get_logs, delete_server, save_server_info, \
|
|
DEFAULT_CONFIG
|
|
|
|
api = Blueprint('api', __name__)
|
|
|
|
client = docker.from_env()
|
|
|
|
|
|
# Server Deployment
|
|
@api.route('/setup', methods=['POST'])
|
|
@oidc.require_login
|
|
def setup_server():
|
|
data = request.get_json()
|
|
username = oidc.user_getfield('preferred_username')
|
|
type_ = data['type']
|
|
version = data['version']
|
|
|
|
path = f"./servers/mc-{username}"
|
|
os.makedirs(path, exist_ok=True)
|
|
save_server_info(username, type_, version)
|
|
|
|
return jsonify({'success': True})
|
|
|
|
|
|
@api.route('/delete', methods=['POST'])
|
|
@oidc.require_login
|
|
def delete():
|
|
username = oidc.user_getfield('preferred_username')
|
|
|
|
if not username:
|
|
return jsonify({"error": "Brak nazwy użytkownika"}), 400
|
|
|
|
result = delete_server(username)
|
|
return jsonify({"message": result})
|
|
|
|
|
|
# Server Controls
|
|
@api.route('/start', methods=['POST'])
|
|
@oidc.require_login
|
|
def start():
|
|
username = oidc.user_getfield('preferred_username')
|
|
setup_file_path = f"./servers/mc-{username}/server_info.json"
|
|
|
|
if not os.path.exists(setup_file_path):
|
|
return jsonify({"error": "Server setup file not found."}), 400
|
|
|
|
with open(setup_file_path, 'r') as file:
|
|
server_info = json.load(file)
|
|
|
|
server_type = server_info.get('type')
|
|
server_version = server_info.get('version')
|
|
|
|
if not server_type or not server_version:
|
|
return jsonify({"error": "Invalid server info."}), 400
|
|
|
|
start_server(username)
|
|
|
|
return jsonify({"status": "started"})
|
|
|
|
|
|
@api.route('/stop', methods=['POST'])
|
|
@oidc.require_login
|
|
def stop():
|
|
username = oidc.user_getfield('preferred_username')
|
|
stop_server(username)
|
|
return jsonify({"status": "stopped"})
|
|
|
|
|
|
@api.route('/restart', methods=['POST'])
|
|
@oidc.require_login
|
|
def restart():
|
|
username = oidc.user_getfield('preferred_username')
|
|
restart_server(username)
|
|
return jsonify({"status": "restarted"})
|
|
|
|
|
|
@api.route('/logs', methods=['GET'])
|
|
@oidc.require_login
|
|
def logs():
|
|
username = oidc.user_getfield('preferred_username')
|
|
return jsonify({"logs": get_logs(username)})
|
|
|
|
|
|
@api.route('/command', methods=['POST'])
|
|
@oidc.require_login
|
|
def send_command():
|
|
data = request.get_json()
|
|
username = data['username']
|
|
command = data['command']
|
|
|
|
if username != oidc.user_getfield('preferred_username'):
|
|
return jsonify({"error": "Unauthorized request."}), 403
|
|
|
|
container_name = f"mc-{username}"
|
|
|
|
try:
|
|
subprocess.run(
|
|
["docker", "exec", container_name, "rcon-cli", command],
|
|
check=True,
|
|
capture_output=True
|
|
)
|
|
return jsonify(success=True)
|
|
except subprocess.CalledProcessError as e:
|
|
return jsonify(success=False, error=str(e)), 500
|
|
|
|
|
|
# Files APIs (Upload, download, delete)
|
|
@api.route('/files', methods=['GET'])
|
|
@oidc.require_login
|
|
def list_files():
|
|
username = oidc.user_getfield('preferred_username')
|
|
path = request.args.get('path', '')
|
|
|
|
base_path = os.path.abspath(f'./servers/mc-{username}')
|
|
requested_path = os.path.abspath(os.path.join(base_path, path))
|
|
|
|
if not requested_path.startswith(base_path):
|
|
return abort(403) # Prevent directory traversal
|
|
|
|
if not os.path.exists(requested_path):
|
|
return abort(404)
|
|
|
|
entries = []
|
|
for item in os.listdir(requested_path):
|
|
if item == "server_info.json": # Hiding panel-specific files
|
|
continue
|
|
full_path = os.path.join(requested_path, item)
|
|
entries.append({
|
|
'name': item,
|
|
'is_dir': os.path.isdir(full_path)
|
|
})
|
|
|
|
return jsonify(entries)
|
|
|
|
|
|
@api.route('/files/download', methods=['GET'])
|
|
@oidc.require_login
|
|
def download_file():
|
|
username = oidc.user_getfield('preferred_username')
|
|
path = request.args.get('path')
|
|
|
|
base_path = os.path.abspath(f'./servers/mc-{username}')
|
|
file_path = os.path.abspath(os.path.join(base_path, path))
|
|
|
|
if not file_path.startswith(base_path) or not os.path.isfile(file_path):
|
|
return abort(403)
|
|
|
|
directory = os.path.dirname(file_path)
|
|
filename = os.path.basename(file_path)
|
|
return send_from_directory(directory, filename, as_attachment=True)
|
|
|
|
|
|
@api.route('/files/upload', methods=['POST'])
|
|
@oidc.require_login
|
|
def upload_file():
|
|
username = oidc.user_getfield('preferred_username')
|
|
path = request.form.get('path', '')
|
|
file = request.files['files']
|
|
base_path = os.path.abspath(f'./servers/mc-{username}')
|
|
upload_path = os.path.abspath(os.path.join(base_path, path))
|
|
|
|
if not upload_path.startswith(base_path):
|
|
return abort(403)
|
|
|
|
os.makedirs(upload_path, exist_ok=True)
|
|
file.save(os.path.join(upload_path, file.filename))
|
|
|
|
return jsonify({'success': True})
|
|
|
|
|
|
@api.route('/files/delete', methods=['POST'])
|
|
@oidc.require_login
|
|
def delete_file_or_folder():
|
|
data = request.get_json()
|
|
username = data.get('username')
|
|
if username != oidc.user_getfield('preferred_username'):
|
|
return jsonify({"error": "Unauthorized request."}), 403
|
|
|
|
path = data.get('path')
|
|
base_path = os.path.abspath(f'./servers/mc-{username}')
|
|
target_path = os.path.abspath(os.path.join(base_path, path))
|
|
|
|
if not target_path.startswith(base_path):
|
|
return abort(403)
|
|
if not os.path.exists(target_path):
|
|
return jsonify({"error": "Nie znaleziono pliku lub folderu"}), 404
|
|
try:
|
|
if os.path.isdir(target_path):
|
|
shutil.rmtree(target_path)
|
|
else:
|
|
os.remove(target_path)
|
|
return jsonify({"success": True})
|
|
except Exception as e:
|
|
return jsonify({"error": str(e)}), 500
|
|
|
|
|
|
# Server config
|
|
@api.route('/config')
|
|
@oidc.require_login
|
|
def get_config():
|
|
username = oidc.user_getfield('preferred_username')
|
|
server_info_path = f'./servers/mc-{username}/server_info.json'
|
|
if not os.path.exists(server_info_path):
|
|
return jsonify({"success": False, "message": "Server config not found"})
|
|
with open(server_info_path, 'r') as f:
|
|
server_info = json.load(f)
|
|
|
|
return jsonify({"success": True, "config": server_info["config"], "version": server_info["version"],
|
|
"type": server_info["type"]})
|
|
|
|
|
|
@api.route('/config', methods=['POST'])
|
|
@oidc.require_login
|
|
def update_config():
|
|
data = request.json
|
|
username = data.get('username')
|
|
if username != oidc.user_getfield('preferred_username'):
|
|
return jsonify({"error": "Unauthorized request."}), 403
|
|
|
|
incoming_config = data.get('config', {})
|
|
server_info_path = f'./servers/mc-{username}/server_info.json'
|
|
if os.path.exists(server_info_path):
|
|
with open(server_info_path, 'r') as f:
|
|
server_info = json.load(f)
|
|
else:
|
|
server_info = DEFAULT_CONFIG.copy()
|
|
|
|
for key in ["type", "version"]:
|
|
if key in incoming_config:
|
|
server_info[key] = incoming_config.pop(key)
|
|
|
|
formatted_config = {key.replace('_', '-'): value for key, value in incoming_config.items()}
|
|
server_info["config"] = formatted_config
|
|
with open(server_info_path, 'w') as f:
|
|
json.dump(server_info, f, indent=4)
|
|
|
|
return jsonify({"success": True})
|
|
|
|
|
|
# Server stats
|
|
@api.route("/status")
|
|
def status():
|
|
username = request.args.get("username")
|
|
container_name = f"mc-{username}"
|
|
try:
|
|
container = client.containers.get(container_name)
|
|
return jsonify(running=container.status == "running")
|
|
except docker.errors.NotFound:
|
|
return jsonify(running=False)
|
|
|
|
|
|
@api.route('/stats', methods=['GET'])
|
|
@oidc.require_login
|
|
def stats():
|
|
username = oidc.user_getfield("preferred_username")
|
|
container_name = f"mc-{username}"
|
|
|
|
try:
|
|
container = client.containers.get(container_name)
|
|
stats = container.stats(stream=False)
|
|
|
|
# RAM (MB)
|
|
memory_usage = stats['memory_stats']['usage'] / (1024 * 1024)
|
|
|
|
# CPU %
|
|
cpu_delta = stats['cpu_stats']['cpu_usage']['total_usage'] - stats['precpu_stats']['cpu_usage']['total_usage']
|
|
system_delta = stats['cpu_stats']['system_cpu_usage'] - stats['precpu_stats']['system_cpu_usage']
|
|
percpu = stats['cpu_stats']['cpu_usage'].get('percpu_usage')
|
|
cpu_count = len(percpu) if percpu else 1
|
|
cpu_usage = (cpu_delta / system_delta) * cpu_count * 100 if system_delta > 0 else 0
|
|
|
|
# Disk Usage (GB)
|
|
server_path = f"./servers/{container_name}"
|
|
total_size = sum(
|
|
os.path.getsize(os.path.join(dp, f)) for dp, dn, filenames in os.walk(server_path) for f in filenames
|
|
) / (1024 * 1024 * 1024)
|
|
disk_usage = min(total_size, 15)
|
|
|
|
return jsonify({
|
|
"cpu": round(cpu_usage, 2),
|
|
"ram": round(memory_usage, 2),
|
|
"disk": round(disk_usage, 2),
|
|
"disk_max": 15
|
|
})
|
|
except docker.errors.NotFound:
|
|
return jsonify({"error": "Container not found"}), 404
|
|
|
|
|
|
# Modrinth
|
|
@api.route('/modrinth/search')
|
|
def modrinth_search():
|
|
query = request.args.get("query")
|
|
server_type = request.args.get("type") # 'fabric', 'paper', etc.
|
|
version = request.args.get("version")
|
|
|
|
categories = {
|
|
'fabric': 'mod',
|
|
'paper': 'plugin'
|
|
}
|
|
|
|
category = categories.get(server_type, 'mod') # fallback
|
|
response = requests.get(
|
|
f"https://api.modrinth.com/v2/search",
|
|
params={
|
|
"query": query,
|
|
"facets": f'[["project_type:{category}"],["categories:{server_type}"],["versions:{version}"]]',
|
|
"limit": 10
|
|
}
|
|
)
|
|
return jsonify(response.json())
|
|
|
|
|
|
@api.route('/modrinth/download', methods=['POST'])
|
|
@oidc.require_login
|
|
def download_mod():
|
|
data = request.json
|
|
project_id = data['project_id']
|
|
username = data['username']
|
|
server_type = data['type']
|
|
|
|
version_response = requests.get(f"https://api.modrinth.com/v2/project/{project_id}/version")
|
|
version_data = version_response.json()
|
|
|
|
file_url = version_data[0]['files'][0]['url']
|
|
file_name = version_data[0]['files'][0]['filename']
|
|
|
|
folder = 'plugins' if server_type == 'paper' else 'mods'
|
|
file_path = f"./servers/mc-{username}/{folder}/{file_name}"
|
|
|
|
with requests.get(file_url, stream=True) as r:
|
|
with open(file_path, 'wb') as f:
|
|
for chunk in r.iter_content(chunk_size=8192):
|
|
f.write(chunk)
|
|
|
|
return jsonify({"success": True, "file": file_name})
|