mcmanager 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- mcmanager/__init__.py +0 -0
- mcmanager/asgi.py +16 -0
- mcmanager/cli.py +115 -0
- mcmanager/configs/server.properties +33 -0
- mcmanager/console/__init__.py +0 -0
- mcmanager/console/admin.py +29 -0
- mcmanager/console/apps.py +22 -0
- mcmanager/console/models.py +112 -0
- mcmanager/console/static/css/font-awesome.css +2337 -0
- mcmanager/console/static/css/font-awesome.min.css +4 -0
- mcmanager/console/static/css/styles.css +3 -0
- mcmanager/console/static/css/tailwind.css +743 -0
- mcmanager/console/static/css/toastify.min.css +15 -0
- mcmanager/console/static/fonts/fontawesome-webfont.ttf +0 -0
- mcmanager/console/static/fonts/fontawesome-webfont.woff +0 -0
- mcmanager/console/static/fonts/fontawesome-webfont.woff2 +0 -0
- mcmanager/console/static/img/favicon.ico +0 -0
- mcmanager/console/static/js/toastify-js.js +15 -0
- mcmanager/console/templates/console/index.html +223 -0
- mcmanager/console/templates/index.html +42 -0
- mcmanager/console/tests.py +3 -0
- mcmanager/console/urls.py +14 -0
- mcmanager/console/views.py +175 -0
- mcmanager/settings.py +158 -0
- mcmanager/urls.py +11 -0
- mcmanager/wsgi.py +16 -0
- mcmanager-0.1.0.dist-info/METADATA +106 -0
- mcmanager-0.1.0.dist-info/RECORD +31 -0
- mcmanager-0.1.0.dist-info/WHEEL +5 -0
- mcmanager-0.1.0.dist-info/entry_points.txt +2 -0
- mcmanager-0.1.0.dist-info/top_level.txt +1 -0
mcmanager/__init__.py
ADDED
|
File without changes
|
mcmanager/asgi.py
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"""
|
|
2
|
+
ASGI config for mcmanager project.
|
|
3
|
+
|
|
4
|
+
It exposes the ASGI callable as a module-level variable named ``application``.
|
|
5
|
+
|
|
6
|
+
For more information on this file, see
|
|
7
|
+
https://docs.djangoproject.com/en/4.0/howto/deployment/asgi/
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import os
|
|
11
|
+
|
|
12
|
+
from django.core.asgi import get_asgi_application
|
|
13
|
+
|
|
14
|
+
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'mcmanager.settings')
|
|
15
|
+
|
|
16
|
+
application = get_asgi_application()
|
mcmanager/cli.py
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import sys
|
|
3
|
+
import argparse
|
|
4
|
+
import shutil
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
def get_data_dir():
|
|
8
|
+
"""Determina o diretório de dados com base na variável de ambiente ou home"""
|
|
9
|
+
if os.environ.get("MCMANAGER_DATA_DIR"):
|
|
10
|
+
return Path(os.environ["MCMANAGER_DATA_DIR"]).resolve()
|
|
11
|
+
|
|
12
|
+
debug_mode = os.environ.get("MCMANAGER_DEBUG", "False").lower() in ("true", "1", "yes")
|
|
13
|
+
if debug_mode:
|
|
14
|
+
# Se estiver em modo debug local, usa a pasta do próprio projeto
|
|
15
|
+
return Path(__file__).resolve().parent.parent
|
|
16
|
+
else:
|
|
17
|
+
return Path.home() / ".mcmanager"
|
|
18
|
+
|
|
19
|
+
def main():
|
|
20
|
+
parser = argparse.ArgumentParser(
|
|
21
|
+
description="mcmanager - Painel de Controle de Servidores Minecraft"
|
|
22
|
+
)
|
|
23
|
+
subparsers = parser.add_subparsers(dest="command", required=True, help="Subcomando a executar")
|
|
24
|
+
|
|
25
|
+
# Comando init
|
|
26
|
+
init_parser = subparsers.add_parser("init", help="Inicializa o diretório de dados, banco e arquivos padrões")
|
|
27
|
+
|
|
28
|
+
# Comando run
|
|
29
|
+
run_parser = subparsers.add_parser("run", help="Inicia o servidor web do painel")
|
|
30
|
+
run_parser.add_argument("--host", default="0.0.0.0", help="Endereço IP para escutar (padrão: 0.0.0.0)")
|
|
31
|
+
run_parser.add_argument("--port", type=int, default=8000, help="Porta para escutar (padrão: 8000)")
|
|
32
|
+
|
|
33
|
+
# Comando createsuperuser
|
|
34
|
+
createsuperuser_parser = subparsers.add_parser(
|
|
35
|
+
"createsuperuser", help="Cria um usuário administrador para o painel"
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
# Comando shell
|
|
39
|
+
shell_parser = subparsers.add_parser("shell", help="Inicia o console interativo do Django")
|
|
40
|
+
|
|
41
|
+
args = parser.parse_args()
|
|
42
|
+
|
|
43
|
+
# 1. Obter e criar estrutura de dados do usuário
|
|
44
|
+
data_dir = get_data_dir()
|
|
45
|
+
data_dir.mkdir(parents=True, exist_ok=True)
|
|
46
|
+
(data_dir / "servers").mkdir(exist_ok=True)
|
|
47
|
+
(data_dir / "jar").mkdir(exist_ok=True)
|
|
48
|
+
(data_dir / "configs").mkdir(exist_ok=True)
|
|
49
|
+
|
|
50
|
+
# 2. Copiar server.properties padrão se não existir
|
|
51
|
+
properties_dest = data_dir / "configs" / "server.properties"
|
|
52
|
+
if not properties_dest.exists():
|
|
53
|
+
# Tenta carregar usando importlib.resources (PEP 517)
|
|
54
|
+
copied = False
|
|
55
|
+
try:
|
|
56
|
+
from importlib import resources
|
|
57
|
+
# Python 3.9+
|
|
58
|
+
source_path = resources.files("mcmanager").joinpath("configs/server.properties")
|
|
59
|
+
with source_path.open("rb") as fsrc:
|
|
60
|
+
with open(properties_dest, "wb") as fdst:
|
|
61
|
+
shutil.copyfileobj(fsrc, fdst)
|
|
62
|
+
copied = True
|
|
63
|
+
except Exception:
|
|
64
|
+
pass
|
|
65
|
+
|
|
66
|
+
if not copied:
|
|
67
|
+
# Fallback para caminho relativo
|
|
68
|
+
source_path = Path(__file__).resolve().parent / "configs" / "server.properties"
|
|
69
|
+
if source_path.exists():
|
|
70
|
+
shutil.copy(source_path, properties_dest)
|
|
71
|
+
copied = True
|
|
72
|
+
|
|
73
|
+
if copied:
|
|
74
|
+
print(f"[*] Arquivo server.properties padrão copiado para: {properties_dest}")
|
|
75
|
+
else:
|
|
76
|
+
print("[!] Aviso: Não foi possível copiar o arquivo server.properties padrão.")
|
|
77
|
+
|
|
78
|
+
# 3. Configurar variáveis de ambiente do Django
|
|
79
|
+
os.environ.setdefault("MCMANAGER_DATA_DIR", str(data_dir))
|
|
80
|
+
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "mcmanager.settings")
|
|
81
|
+
|
|
82
|
+
# 4. Inicializar Django
|
|
83
|
+
import django
|
|
84
|
+
django.setup()
|
|
85
|
+
|
|
86
|
+
from django.core.management import call_command
|
|
87
|
+
|
|
88
|
+
# 5. Executar os subcomandos
|
|
89
|
+
if args.command == "init":
|
|
90
|
+
print("[*] Executando migrações do banco de dados...")
|
|
91
|
+
call_command("migrate", interactive=False)
|
|
92
|
+
print("[*] Coletando arquivos estáticos...")
|
|
93
|
+
call_command("collectstatic", "--noinput", "--clear")
|
|
94
|
+
print("\n[+] Inicialização concluída com sucesso!")
|
|
95
|
+
print(f"[+] Seus dados estão em: {data_dir}")
|
|
96
|
+
print("[+] Agora você pode rodar: mcmanager createsuperuser")
|
|
97
|
+
|
|
98
|
+
elif args.command == "run":
|
|
99
|
+
bind_address = f"{args.host}:{args.port}"
|
|
100
|
+
print(f"[*] Iniciando servidor mcmanager em http://{bind_address}/")
|
|
101
|
+
print("[*] Pressione CTRL+C para encerrar.")
|
|
102
|
+
# Em produção, rodamos sem autoreload
|
|
103
|
+
call_command("runserver", "--noreload", "--nostatic", bind_address)
|
|
104
|
+
|
|
105
|
+
elif args.command == "createsuperuser":
|
|
106
|
+
try:
|
|
107
|
+
call_command("createsuperuser")
|
|
108
|
+
except KeyboardInterrupt:
|
|
109
|
+
print("\n[!] Operação cancelada.")
|
|
110
|
+
|
|
111
|
+
elif args.command == "shell":
|
|
112
|
+
call_command("shell")
|
|
113
|
+
|
|
114
|
+
if __name__ == "__main__":
|
|
115
|
+
main()
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
#Minecraft server properties
|
|
2
|
+
#Wed Jul 10 19:30:06 UTC 2024
|
|
3
|
+
generator-settings=
|
|
4
|
+
op-permission-level=4
|
|
5
|
+
allow-nether=true
|
|
6
|
+
level-name=world
|
|
7
|
+
enable-query=false
|
|
8
|
+
allow-flight=true
|
|
9
|
+
announce-player-achievements=true
|
|
10
|
+
server-port=25566
|
|
11
|
+
level-type=DEFAULT
|
|
12
|
+
enable-rcon=false
|
|
13
|
+
force-gamemode=false
|
|
14
|
+
level-seed=
|
|
15
|
+
server-ip=
|
|
16
|
+
max-build-height=256
|
|
17
|
+
spawn-npcs=true
|
|
18
|
+
white-list=false
|
|
19
|
+
spawn-animals=true
|
|
20
|
+
hardcore=false
|
|
21
|
+
snooper-enabled=true
|
|
22
|
+
online-mode=false
|
|
23
|
+
resource-pack=
|
|
24
|
+
pvp=true
|
|
25
|
+
difficulty=1
|
|
26
|
+
enable-command-block=false
|
|
27
|
+
gamemode=0
|
|
28
|
+
player-idle-timeout=0
|
|
29
|
+
max-players=5
|
|
30
|
+
spawn-monsters=true
|
|
31
|
+
generate-structures=true
|
|
32
|
+
view-distance=10
|
|
33
|
+
motd=A Minecraft Server
|
|
File without changes
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
from typing import Any
|
|
2
|
+
from django.contrib import admin
|
|
3
|
+
from django.http import HttpRequest
|
|
4
|
+
from django.http.response import HttpResponse
|
|
5
|
+
from .models import Server, Type
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@admin.register(Server)
|
|
9
|
+
class ServerAdmin(admin.ModelAdmin):
|
|
10
|
+
list_display = ('name', 'port', 'type', 'status')
|
|
11
|
+
exclude = ('jar', 'server_properties')
|
|
12
|
+
search_fields = ('name', 'port', 'type')
|
|
13
|
+
list_filter = ('type', 'status')
|
|
14
|
+
readonly_fields = ('status',)
|
|
15
|
+
|
|
16
|
+
def get_form(self, request, obj=..., change=..., **kwargs) -> Any:
|
|
17
|
+
if obj is None:
|
|
18
|
+
self.exclude = ('jar', 'server_properties')
|
|
19
|
+
self.readonly_fields = ('status',)
|
|
20
|
+
else:
|
|
21
|
+
self.exclude = None
|
|
22
|
+
self.readonly_fields = ('status', 'jar')
|
|
23
|
+
return super().get_form(request, obj, change, **kwargs)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@admin.register(Type)
|
|
27
|
+
class TypeAdmin(admin.ModelAdmin):
|
|
28
|
+
list_display = ('name',)
|
|
29
|
+
search_fields = ('name',)
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
from django.apps import AppConfig
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class ConsoleConfig(AppConfig):
|
|
5
|
+
default_auto_field = 'django.db.models.BigAutoField'
|
|
6
|
+
name = 'mcmanager.console'
|
|
7
|
+
|
|
8
|
+
def ready(self) -> None:
|
|
9
|
+
try:
|
|
10
|
+
print('Checking server status...')
|
|
11
|
+
from .views import is_server_running
|
|
12
|
+
from .models import Server
|
|
13
|
+
for server in Server.objects.all():
|
|
14
|
+
if is_server_running(server.id):
|
|
15
|
+
server.status = True
|
|
16
|
+
server.save()
|
|
17
|
+
else:
|
|
18
|
+
server.status = False
|
|
19
|
+
server.save()
|
|
20
|
+
print('Server status checked!')
|
|
21
|
+
except Exception as e:
|
|
22
|
+
print(e)
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import uuid
|
|
2
|
+
import os
|
|
3
|
+
import shutil
|
|
4
|
+
from django.db import models
|
|
5
|
+
from django.conf import settings
|
|
6
|
+
from django.dispatch import receiver
|
|
7
|
+
from django.db.models.signals import post_save, pre_save, pre_delete
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def get_jar_files():
|
|
11
|
+
jar_dir = getattr(settings, 'JAR_DIR', os.path.join(settings.BASE_DIR, 'jar'))
|
|
12
|
+
if not os.path.exists(jar_dir):
|
|
13
|
+
os.makedirs(jar_dir)
|
|
14
|
+
return [(x, x) for x in os.listdir(jar_dir) if x.endswith('.jar')]
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def get_default_server_prop():
|
|
18
|
+
configs_dir = getattr(settings, 'CONFIGS_DIR', os.path.join(settings.BASE_DIR, 'configs'))
|
|
19
|
+
with open(os.path.join(configs_dir, 'server.properties'), 'r', encoding='utf-8') as arq:
|
|
20
|
+
text = arq.read()
|
|
21
|
+
return text
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class Type(models.Model):
|
|
25
|
+
name = models.CharField(max_length=100)
|
|
26
|
+
dependencies = models.JSONField(blank=True, null=True)
|
|
27
|
+
created_at = models.DateTimeField(auto_now_add=True)
|
|
28
|
+
updated_at = models.DateTimeField(auto_now=True)
|
|
29
|
+
|
|
30
|
+
def __str__(self):
|
|
31
|
+
return self.name
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class Server(models.Model):
|
|
35
|
+
name = models.CharField(max_length=100)
|
|
36
|
+
jar_template = models.CharField(max_length=100, choices=get_jar_files())
|
|
37
|
+
jar = models.CharField(max_length=100, blank=True, null=True)
|
|
38
|
+
# ip = models.GenericIPAddressField(blank=True, null=True)
|
|
39
|
+
port = models.IntegerField(default=25565)
|
|
40
|
+
memory_limit = models.IntegerField("Memory Limit (MB)", default=1024)
|
|
41
|
+
created_at = models.DateTimeField(auto_now_add=True)
|
|
42
|
+
updated_at = models.DateTimeField(auto_now=True)
|
|
43
|
+
type = models.ForeignKey(
|
|
44
|
+
Type, on_delete=models.CASCADE, related_name='servers')
|
|
45
|
+
status = models.BooleanField(default=False)
|
|
46
|
+
server_properties = models.TextField(blank=True, null=True)
|
|
47
|
+
|
|
48
|
+
def __str__(self):
|
|
49
|
+
return self.name
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
@receiver(pre_delete, sender=Server)
|
|
53
|
+
def pre_delete_server(sender, instance, **kwargs):
|
|
54
|
+
servers_dir = getattr(settings, 'SERVERS_DIR', os.path.join(settings.BASE_DIR, 'servers'))
|
|
55
|
+
server_path = os.path.join(servers_dir, f'server_{instance.id}')
|
|
56
|
+
if os.path.exists(server_path):
|
|
57
|
+
shutil.rmtree(server_path)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
pre_delete.connect(pre_delete_server, sender=Server)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
@receiver(post_save, sender=Server)
|
|
64
|
+
def post_save_server(sender, instance, **kwargs):
|
|
65
|
+
servers_dir = getattr(settings, 'SERVERS_DIR', os.path.join(settings.BASE_DIR, 'servers'))
|
|
66
|
+
jar_dir = getattr(settings, 'JAR_DIR', os.path.join(settings.BASE_DIR, 'jar'))
|
|
67
|
+
configs_dir = getattr(settings, 'CONFIGS_DIR', os.path.join(settings.BASE_DIR, 'configs'))
|
|
68
|
+
|
|
69
|
+
server_path = os.path.join(servers_dir, f'server_{instance.id}')
|
|
70
|
+
properties_path = os.path.join(server_path, 'server.properties')
|
|
71
|
+
|
|
72
|
+
if not instance.jar:
|
|
73
|
+
instance.jar = str(instance.id) + "_" + instance.jar_template
|
|
74
|
+
os.makedirs(server_path, exist_ok=True)
|
|
75
|
+
shutil.copy(os.path.join(jar_dir, instance.jar_template), os.path.join(server_path, instance.jar))
|
|
76
|
+
shutil.copy(os.path.join(configs_dir, 'server.properties'), properties_path)
|
|
77
|
+
|
|
78
|
+
if instance.type.dependencies:
|
|
79
|
+
for dependency in instance.type.dependencies:
|
|
80
|
+
dep_source = os.path.join(jar_dir, dependency)
|
|
81
|
+
dep_dest = os.path.join(server_path, dependency)
|
|
82
|
+
if os.path.isdir(dep_source):
|
|
83
|
+
shutil.copytree(dep_source, dep_dest)
|
|
84
|
+
else:
|
|
85
|
+
shutil.copy(dep_source, dep_dest)
|
|
86
|
+
|
|
87
|
+
with open(os.path.join(server_path, 'eula.txt'), 'w', encoding='utf-8') as f:
|
|
88
|
+
f.write('eula=true')
|
|
89
|
+
|
|
90
|
+
new_server_properties = []
|
|
91
|
+
with open(properties_path, 'r', encoding='utf-8', errors='ignore') as f:
|
|
92
|
+
for line in f:
|
|
93
|
+
if line.startswith('server-port='):
|
|
94
|
+
new_server_properties.append(f'server-port={instance.port}\n')
|
|
95
|
+
else:
|
|
96
|
+
new_server_properties.append(line)
|
|
97
|
+
|
|
98
|
+
with open(properties_path, 'w', encoding='utf-8') as f:
|
|
99
|
+
f.write(''.join(new_server_properties))
|
|
100
|
+
|
|
101
|
+
with open(properties_path, 'r', encoding='utf-8', errors='ignore') as f:
|
|
102
|
+
text = f.read()
|
|
103
|
+
|
|
104
|
+
instance.server_properties = text
|
|
105
|
+
instance.save()
|
|
106
|
+
else:
|
|
107
|
+
with open(properties_path, 'w', encoding='utf-8') as f:
|
|
108
|
+
f.write(instance.server_properties)
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
post_save.connect(post_save_server, sender=Server)
|
|
112
|
+
|