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 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
+