mcmanager 0.1.2__tar.gz → 0.1.4__tar.gz
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-0.1.2 → mcmanager-0.1.4}/PKG-INFO +5 -2
- {mcmanager-0.1.2 → mcmanager-0.1.4}/mcmanager/cli.py +135 -115
- mcmanager-0.1.4/mcmanager/console/admin.py +54 -0
- mcmanager-0.1.4/mcmanager/console/apps.py +10 -0
- mcmanager-0.1.4/mcmanager/console/compat.py +31 -0
- mcmanager-0.1.4/mcmanager/console/forms.py +15 -0
- mcmanager-0.1.4/mcmanager/console/json_utils.py +8 -0
- mcmanager-0.1.4/mcmanager/console/management/__init__.py +0 -0
- mcmanager-0.1.4/mcmanager/console/management/commands/__init__.py +0 -0
- mcmanager-0.1.4/mcmanager/console/management/commands/format_code.py +43 -0
- mcmanager-0.1.4/mcmanager/console/management/commands/generate_secret_key.py +23 -0
- mcmanager-0.1.4/mcmanager/console/migrations/0001_initial.py +45 -0
- mcmanager-0.1.4/mcmanager/console/migrations/0002_alter_server_ip_alter_server_port.py +23 -0
- mcmanager-0.1.4/mcmanager/console/migrations/0003_remove_server_ip_server_status.py +22 -0
- mcmanager-0.1.4/mcmanager/console/migrations/0004_server_server_properties.py +18 -0
- mcmanager-0.1.4/mcmanager/console/migrations/0005_alter_server_jar_template_and_more.py +23 -0
- mcmanager-0.1.4/mcmanager/console/migrations/0006_server_rcon_password_server_rcon_port.py +68 -0
- mcmanager-0.1.4/mcmanager/console/migrations/0007_remove_server_status.py +17 -0
- mcmanager-0.1.4/mcmanager/console/migrations/0008_jardownload.py +26 -0
- mcmanager-0.1.4/mcmanager/console/migrations/0009_backup.py +26 -0
- mcmanager-0.1.4/mcmanager/console/migrations/0010_server_auto_restart_enabled_and_more.py +38 -0
- mcmanager-0.1.4/mcmanager/console/migrations/__init__.py +0 -0
- mcmanager-0.1.4/mcmanager/console/models.py +118 -0
- mcmanager-0.1.4/mcmanager/console/services/__init__.py +0 -0
- mcmanager-0.1.4/mcmanager/console/services/backups.py +137 -0
- mcmanager-0.1.4/mcmanager/console/services/dashboard.py +49 -0
- mcmanager-0.1.4/mcmanager/console/services/doctor.py +82 -0
- mcmanager-0.1.4/mcmanager/console/services/jar_providers/__init__.py +0 -0
- mcmanager-0.1.4/mcmanager/console/services/jar_providers/base.py +18 -0
- mcmanager-0.1.4/mcmanager/console/services/jar_providers/mojang.py +36 -0
- mcmanager-0.1.4/mcmanager/console/services/jar_providers/paper.py +32 -0
- mcmanager-0.1.4/mcmanager/console/services/jars.py +72 -0
- mcmanager-0.1.4/mcmanager/console/services/process.py +214 -0
- mcmanager-0.1.4/mcmanager/console/services/provisioning.py +77 -0
- mcmanager-0.1.4/mcmanager/console/services/rcon.py +86 -0
- mcmanager-0.1.4/mcmanager/console/services/supervisor.py +93 -0
- {mcmanager-0.1.2 → mcmanager-0.1.4}/mcmanager/console/static/css/tailwind.css +813 -742
- {mcmanager-0.1.2 → mcmanager-0.1.4}/mcmanager/console/templates/console/index.html +149 -13
- mcmanager-0.1.4/mcmanager/console/templates/console/jars.html +122 -0
- mcmanager-0.1.4/mcmanager/console/templates/index.html +152 -0
- mcmanager-0.1.4/mcmanager/console/urls.py +23 -0
- mcmanager-0.1.4/mcmanager/console/views.py +149 -0
- mcmanager-0.1.4/mcmanager/console/views_backups.py +63 -0
- mcmanager-0.1.4/mcmanager/console/views_jars.py +54 -0
- {mcmanager-0.1.2 → mcmanager-0.1.4}/mcmanager/settings.py +8 -0
- {mcmanager-0.1.2 → mcmanager-0.1.4}/mcmanager.egg-info/PKG-INFO +5 -2
- mcmanager-0.1.4/mcmanager.egg-info/SOURCES.txt +70 -0
- {mcmanager-0.1.2 → mcmanager-0.1.4}/mcmanager.egg-info/requires.txt +4 -0
- {mcmanager-0.1.2 → mcmanager-0.1.4}/pyproject.toml +46 -36
- {mcmanager-0.1.2 → mcmanager-0.1.4}/requirements.txt +2 -2
- mcmanager-0.1.2/mcmanager/console/admin.py +0 -29
- mcmanager-0.1.2/mcmanager/console/apps.py +0 -22
- mcmanager-0.1.2/mcmanager/console/models.py +0 -112
- mcmanager-0.1.2/mcmanager/console/templates/index.html +0 -42
- mcmanager-0.1.2/mcmanager/console/tests.py +0 -3
- mcmanager-0.1.2/mcmanager/console/urls.py +0 -14
- mcmanager-0.1.2/mcmanager/console/views.py +0 -175
- mcmanager-0.1.2/mcmanager.egg-info/SOURCES.txt +0 -37
- {mcmanager-0.1.2 → mcmanager-0.1.4}/MANIFEST.in +0 -0
- {mcmanager-0.1.2 → mcmanager-0.1.4}/README.md +0 -0
- {mcmanager-0.1.2 → mcmanager-0.1.4}/mcmanager/__init__.py +0 -0
- {mcmanager-0.1.2 → mcmanager-0.1.4}/mcmanager/__main__.py +0 -0
- {mcmanager-0.1.2 → mcmanager-0.1.4}/mcmanager/asgi.py +0 -0
- {mcmanager-0.1.2 → mcmanager-0.1.4}/mcmanager/configs/server.properties +0 -0
- {mcmanager-0.1.2 → mcmanager-0.1.4}/mcmanager/console/__init__.py +0 -0
- {mcmanager-0.1.2 → mcmanager-0.1.4}/mcmanager/console/static/css/font-awesome.css +0 -0
- {mcmanager-0.1.2 → mcmanager-0.1.4}/mcmanager/console/static/css/font-awesome.min.css +0 -0
- {mcmanager-0.1.2 → mcmanager-0.1.4}/mcmanager/console/static/css/styles.css +0 -0
- {mcmanager-0.1.2 → mcmanager-0.1.4}/mcmanager/console/static/css/toastify.min.css +0 -0
- {mcmanager-0.1.2 → mcmanager-0.1.4}/mcmanager/console/static/fonts/fontawesome-webfont.ttf +0 -0
- {mcmanager-0.1.2 → mcmanager-0.1.4}/mcmanager/console/static/fonts/fontawesome-webfont.woff +0 -0
- {mcmanager-0.1.2 → mcmanager-0.1.4}/mcmanager/console/static/fonts/fontawesome-webfont.woff2 +0 -0
- {mcmanager-0.1.2 → mcmanager-0.1.4}/mcmanager/console/static/img/favicon.ico +0 -0
- {mcmanager-0.1.2 → mcmanager-0.1.4}/mcmanager/console/static/js/toastify-js.js +0 -0
- {mcmanager-0.1.2 → mcmanager-0.1.4}/mcmanager/urls.py +0 -0
- {mcmanager-0.1.2 → mcmanager-0.1.4}/mcmanager/wsgi.py +0 -0
- {mcmanager-0.1.2 → mcmanager-0.1.4}/mcmanager.egg-info/dependency_links.txt +0 -0
- {mcmanager-0.1.2 → mcmanager-0.1.4}/mcmanager.egg-info/entry_points.txt +0 -0
- {mcmanager-0.1.2 → mcmanager-0.1.4}/mcmanager.egg-info/top_level.txt +0 -0
- {mcmanager-0.1.2 → mcmanager-0.1.4}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mcmanager
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.4
|
|
4
4
|
Summary: A Django-based web interface to manage Minecraft servers on a VPS
|
|
5
5
|
License: MIT
|
|
6
6
|
Classifier: Programming Language :: Python :: 3
|
|
@@ -8,11 +8,14 @@ Classifier: Framework :: Django
|
|
|
8
8
|
Classifier: License :: OSI Approved :: MIT License
|
|
9
9
|
Classifier: Operating System :: POSIX :: Linux
|
|
10
10
|
Classifier: Topic :: Games/Entertainment
|
|
11
|
-
Requires-Python: >=3.
|
|
11
|
+
Requires-Python: >=3.10
|
|
12
12
|
Description-Content-Type: text/markdown
|
|
13
13
|
Requires-Dist: Django<5.2.0,>=4.2.0
|
|
14
14
|
Requires-Dist: psutil>=5.8.0
|
|
15
15
|
Requires-Dist: whitenoise>=6.0.0
|
|
16
|
+
Provides-Extra: dev
|
|
17
|
+
Requires-Dist: pytest>=7.4.0; extra == "dev"
|
|
18
|
+
Requires-Dist: pytest-django>=4.7.0; extra == "dev"
|
|
16
19
|
|
|
17
20
|
# mcmanager (BETA)
|
|
18
21
|
|
|
@@ -1,115 +1,135 @@
|
|
|
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
|
-
|
|
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
|
-
|
|
35
|
-
"createsuperuser", help="Cria um usuário administrador para o painel"
|
|
36
|
-
)
|
|
37
|
-
|
|
38
|
-
# Comando shell
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
if copied:
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
print("[
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
elif args.command == "
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
call_command("
|
|
113
|
-
|
|
114
|
-
|
|
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
|
+
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
|
+
subparsers.add_parser(
|
|
35
|
+
"createsuperuser", help="Cria um usuário administrador para o painel"
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
# Comando shell
|
|
39
|
+
subparsers.add_parser("shell", help="Inicia o console interativo do Django")
|
|
40
|
+
|
|
41
|
+
# Comando doctor
|
|
42
|
+
subparsers.add_parser(
|
|
43
|
+
"doctor", help="Diagnostica o ambiente (Java, diretórios, banco de dados)"
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
args = parser.parse_args()
|
|
47
|
+
|
|
48
|
+
# 1. Obter e criar estrutura de dados do usuário
|
|
49
|
+
data_dir = get_data_dir()
|
|
50
|
+
data_dir.mkdir(parents=True, exist_ok=True)
|
|
51
|
+
(data_dir / "servers").mkdir(exist_ok=True)
|
|
52
|
+
(data_dir / "jar").mkdir(exist_ok=True)
|
|
53
|
+
(data_dir / "configs").mkdir(exist_ok=True)
|
|
54
|
+
(data_dir / "run").mkdir(exist_ok=True)
|
|
55
|
+
(data_dir / "backups").mkdir(exist_ok=True)
|
|
56
|
+
|
|
57
|
+
# 2. Copiar server.properties padrão se não existir
|
|
58
|
+
properties_dest = data_dir / "configs" / "server.properties"
|
|
59
|
+
if not properties_dest.exists():
|
|
60
|
+
# Tenta carregar usando importlib.resources (PEP 517)
|
|
61
|
+
copied = False
|
|
62
|
+
try:
|
|
63
|
+
from importlib import resources
|
|
64
|
+
# Python 3.9+
|
|
65
|
+
source_path = resources.files("mcmanager").joinpath("configs/server.properties")
|
|
66
|
+
with source_path.open("rb") as fsrc:
|
|
67
|
+
with open(properties_dest, "wb") as fdst:
|
|
68
|
+
shutil.copyfileobj(fsrc, fdst)
|
|
69
|
+
copied = True
|
|
70
|
+
except Exception:
|
|
71
|
+
pass
|
|
72
|
+
|
|
73
|
+
if not copied:
|
|
74
|
+
# Fallback para caminho relativo
|
|
75
|
+
source_path = Path(__file__).resolve().parent / "configs" / "server.properties"
|
|
76
|
+
if source_path.exists():
|
|
77
|
+
shutil.copy(source_path, properties_dest)
|
|
78
|
+
copied = True
|
|
79
|
+
|
|
80
|
+
if copied:
|
|
81
|
+
print(f"[*] Arquivo server.properties padrão copiado para: {properties_dest}")
|
|
82
|
+
else:
|
|
83
|
+
print("[!] Aviso: Não foi possível copiar o arquivo server.properties padrão.")
|
|
84
|
+
|
|
85
|
+
# 3. Configurar variáveis de ambiente do Django
|
|
86
|
+
os.environ.setdefault("MCMANAGER_DATA_DIR", str(data_dir))
|
|
87
|
+
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "mcmanager.settings")
|
|
88
|
+
|
|
89
|
+
# 4. Inicializar Django
|
|
90
|
+
import django
|
|
91
|
+
django.setup()
|
|
92
|
+
|
|
93
|
+
from django.core.management import call_command
|
|
94
|
+
|
|
95
|
+
# 5. Executar os subcomandos
|
|
96
|
+
if args.command == "init":
|
|
97
|
+
print("[*] Executando migrações do banco de dados...")
|
|
98
|
+
call_command("migrate", interactive=False)
|
|
99
|
+
print("[*] Coletando arquivos estáticos...")
|
|
100
|
+
call_command("collectstatic", "--noinput", "--clear")
|
|
101
|
+
print("\n[+] Inicialização concluída com sucesso!")
|
|
102
|
+
print(f"[+] Seus dados estão em: {data_dir}")
|
|
103
|
+
print("[+] Agora você pode rodar: mcmanager createsuperuser")
|
|
104
|
+
|
|
105
|
+
elif args.command == "run":
|
|
106
|
+
from mcmanager.console.services import supervisor
|
|
107
|
+
supervisor.start()
|
|
108
|
+
bind_address = f"{args.host}:{args.port}"
|
|
109
|
+
print(f"[*] Iniciando servidor mcmanager em http://{bind_address}/")
|
|
110
|
+
print("[*] Pressione CTRL+C para encerrar.")
|
|
111
|
+
# Em produção, rodamos sem autoreload
|
|
112
|
+
call_command("runserver", "--noreload", "--nostatic", bind_address)
|
|
113
|
+
|
|
114
|
+
elif args.command == "createsuperuser":
|
|
115
|
+
try:
|
|
116
|
+
call_command("createsuperuser")
|
|
117
|
+
except KeyboardInterrupt:
|
|
118
|
+
print("\n[!] Operação cancelada.")
|
|
119
|
+
|
|
120
|
+
elif args.command == "shell":
|
|
121
|
+
call_command("shell")
|
|
122
|
+
|
|
123
|
+
elif args.command == "doctor":
|
|
124
|
+
from mcmanager.console.services import doctor
|
|
125
|
+
results = doctor.run_checks()
|
|
126
|
+
all_passed = True
|
|
127
|
+
for result in results:
|
|
128
|
+
symbol = "OK" if result['passed'] else "FAIL"
|
|
129
|
+
print(f"[{symbol}] {result['name']}: {result['message']}")
|
|
130
|
+
if not result['passed']:
|
|
131
|
+
all_passed = False
|
|
132
|
+
sys.exit(0 if all_passed else 1)
|
|
133
|
+
|
|
134
|
+
if __name__ == "__main__":
|
|
135
|
+
main()
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
from typing import Any
|
|
2
|
+
|
|
3
|
+
from django.contrib import admin
|
|
4
|
+
|
|
5
|
+
from .forms import ServerForm
|
|
6
|
+
from .models import Server, Type
|
|
7
|
+
from .services import process, provisioning
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@admin.register(Server)
|
|
11
|
+
class ServerAdmin(admin.ModelAdmin):
|
|
12
|
+
form = ServerForm
|
|
13
|
+
list_display = ('name', 'port', 'type', 'is_running')
|
|
14
|
+
exclude = ('jar', 'server_properties', 'desired_running', 'consecutive_restart_failures', 'last_scheduled_backup_date')
|
|
15
|
+
search_fields = ('name', 'port', 'type')
|
|
16
|
+
list_filter = ('type',)
|
|
17
|
+
readonly_fields = ()
|
|
18
|
+
|
|
19
|
+
def is_running(self, obj):
|
|
20
|
+
return process.is_running(obj)
|
|
21
|
+
is_running.boolean = True
|
|
22
|
+
is_running.short_description = 'Running'
|
|
23
|
+
|
|
24
|
+
def get_form(self, request, obj=..., change=..., **kwargs) -> Any:
|
|
25
|
+
internal_fields = ('desired_running', 'consecutive_restart_failures', 'last_scheduled_backup_date')
|
|
26
|
+
if obj is None:
|
|
27
|
+
self.exclude = ('jar', 'server_properties') + internal_fields
|
|
28
|
+
self.readonly_fields = ()
|
|
29
|
+
else:
|
|
30
|
+
self.exclude = internal_fields
|
|
31
|
+
self.readonly_fields = ('jar',)
|
|
32
|
+
return super().get_form(request, obj, change, **kwargs)
|
|
33
|
+
|
|
34
|
+
def save_model(self, request, obj, form, change):
|
|
35
|
+
super().save_model(request, obj, form, change)
|
|
36
|
+
if not obj.jar:
|
|
37
|
+
provisioning.create_server_files(obj)
|
|
38
|
+
else:
|
|
39
|
+
provisioning.sync_server_properties_file(obj)
|
|
40
|
+
|
|
41
|
+
def delete_model(self, request, obj):
|
|
42
|
+
provisioning.delete_server_files(obj)
|
|
43
|
+
super().delete_model(request, obj)
|
|
44
|
+
|
|
45
|
+
def delete_queryset(self, request, queryset):
|
|
46
|
+
for obj in queryset:
|
|
47
|
+
provisioning.delete_server_files(obj)
|
|
48
|
+
super().delete_queryset(request, queryset)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
@admin.register(Type)
|
|
52
|
+
class TypeAdmin(admin.ModelAdmin):
|
|
53
|
+
list_display = ('name',)
|
|
54
|
+
search_fields = ('name',)
|
|
@@ -0,0 +1,10 @@
|
|
|
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):
|
|
9
|
+
from .compat import patch_context_copy_for_python314
|
|
10
|
+
patch_context_copy_for_python314()
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"""Runtime compatibility shims for this project's supported environments."""
|
|
2
|
+
from django.template.context import BaseContext
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def patch_context_copy_for_python314():
|
|
6
|
+
"""Python 3.14 changed copy.copy()'s handling of `super` proxy objects,
|
|
7
|
+
breaking Django 5.1's BaseContext.__copy__ (which does
|
|
8
|
+
`duplicate = copy(super())` to get a shallow copy of self while bypassing
|
|
9
|
+
dict's own __copy__ -- BaseContext isn't a dict subclass, but relies on
|
|
10
|
+
this idiom to allocate a bare instance). This now raises
|
|
11
|
+
`AttributeError: 'super' object has no attribute 'dicts' and no __dict__
|
|
12
|
+
for setting new attributes` any time Django copies a template Context --
|
|
13
|
+
which happens on every Django admin change-list page render (via the
|
|
14
|
+
`{% change_list_object_tools %}` templatetag), and more generally
|
|
15
|
+
whenever `Context.new()` is called. Reproduced independently of Django
|
|
16
|
+
with a minimal `copy(super())` snippet, so this is an upstream Python
|
|
17
|
+
3.14 regression, not an application bug. Django 5.1.15 (latest 5.1.x as
|
|
18
|
+
of writing) does not include a fix.
|
|
19
|
+
|
|
20
|
+
TODO: remove once either (a) Django ships a fix for the copy(super())
|
|
21
|
+
idiom under Python 3.14+, or (b) this project's supported Python floor
|
|
22
|
+
is raised past whatever version fixes it -- check `python --version`
|
|
23
|
+
and retest by removing this call before deleting it.
|
|
24
|
+
"""
|
|
25
|
+
def _base_context_copy(self):
|
|
26
|
+
duplicate = object.__new__(self.__class__)
|
|
27
|
+
duplicate.__dict__.update(self.__dict__)
|
|
28
|
+
duplicate.dicts = self.dicts[:]
|
|
29
|
+
return duplicate
|
|
30
|
+
|
|
31
|
+
BaseContext.__copy__ = _base_context_copy
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
from django import forms
|
|
2
|
+
|
|
3
|
+
from .models import Server, get_jar_files
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class ServerForm(forms.ModelForm):
|
|
7
|
+
jar_template = forms.ChoiceField(choices=[])
|
|
8
|
+
|
|
9
|
+
class Meta:
|
|
10
|
+
model = Server
|
|
11
|
+
fields = '__all__'
|
|
12
|
+
|
|
13
|
+
def __init__(self, *args, **kwargs):
|
|
14
|
+
super().__init__(*args, **kwargs)
|
|
15
|
+
self.fields['jar_template'].choices = get_jar_files()
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"""Shared JSON error-response helper. Success responses keep their existing,
|
|
2
|
+
per-view flat shape unchanged — only error responses use this, so they get a
|
|
3
|
+
real HTTP status code instead of always returning 200."""
|
|
4
|
+
from django.http import JsonResponse
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def json_error(message, status=400):
|
|
8
|
+
return JsonResponse({'status': 'error', 'message': message}, status=status)
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import subprocess
|
|
2
|
+
from django.core.management.base import BaseCommand
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class Command(BaseCommand):
|
|
6
|
+
"""
|
|
7
|
+
Formatar o código do projeto
|
|
8
|
+
"""
|
|
9
|
+
help = 'Formatar o código do projeto'
|
|
10
|
+
|
|
11
|
+
def handle(self, *args, **kwargs):
|
|
12
|
+
# Executa o comando de formatação para *.py
|
|
13
|
+
result1 = subprocess.getstatusoutput(
|
|
14
|
+
"autopep8 --recursive --in-place --exclude venv,.git,node_modules ./")
|
|
15
|
+
if result1[0] == 0:
|
|
16
|
+
self.stdout.write(self.style.SUCCESS(
|
|
17
|
+
'Código Python formatado com sucesso!'))
|
|
18
|
+
if kwargs.get('verbosity', 1) > 1:
|
|
19
|
+
self.stdout.write(self.style.SUCCESS(result1[1]))
|
|
20
|
+
else:
|
|
21
|
+
self.stdout.write(self.style.ERROR(
|
|
22
|
+
'Erro ao formatar o código Python!'))
|
|
23
|
+
if kwargs.get('verbosity', 1) > 1:
|
|
24
|
+
self.stdout.write(self.style.ERROR(result1[1]))
|
|
25
|
+
# Executa o comando de formatação para *.html, *.css e *.js
|
|
26
|
+
result2 = subprocess.getstatusoutput(
|
|
27
|
+
"""find . -type d \\( -name 'venv' -o -name 'node_modules' -o -name '.git' \\) -prune -false -o -type d -name 'templates' | while read -r dir; do
|
|
28
|
+
echo "Formating $dir"
|
|
29
|
+
djlint "$dir" --reformat --format-css --format-js --use-gitignore
|
|
30
|
+
done""")
|
|
31
|
+
if result2[0] == 0:
|
|
32
|
+
self.stdout.write(self.style.SUCCESS(
|
|
33
|
+
'Não foi necessário formatar o código HTML, CSS e JavaScript!'))
|
|
34
|
+
if kwargs.get('verbosity', 1) > 1:
|
|
35
|
+
self.stdout.write(self.style.SUCCESS(result2[1]))
|
|
36
|
+
else:
|
|
37
|
+
if kwargs.get('verbosity', 1) > 1:
|
|
38
|
+
self.stdout.write(self.style.WARNING(
|
|
39
|
+
'Foram realizadas alterações no código HTML, CSS e JavaScript!'))
|
|
40
|
+
self.stdout.write(self.style.WARNING(result2[1]))
|
|
41
|
+
else:
|
|
42
|
+
self.stdout.write(self.style.WARNING(
|
|
43
|
+
'Foram realizadas alterações no código HTML, CSS e JavaScript!\nUse "python manage.py format_code --verbosity 2" para mais detalhes.'))
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
from django.core.management.base import BaseCommand
|
|
2
|
+
from django.core.management.utils import get_random_secret_key
|
|
3
|
+
from django.conf import settings
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Command(BaseCommand):
|
|
7
|
+
"""
|
|
8
|
+
Gerar uma nova SECRET_KEY e atualizá-la no arquivo .secret_key do diretório de dados
|
|
9
|
+
"""
|
|
10
|
+
help = 'Gerar uma nova SECRET_KEY e atualizá-la no arquivo .secret_key do diretório de dados'
|
|
11
|
+
|
|
12
|
+
def handle(self, *args, **kwargs):
|
|
13
|
+
secret_key = get_random_secret_key()
|
|
14
|
+
user_data_dir = getattr(settings, 'USER_DATA_DIR', settings.BASE_DIR)
|
|
15
|
+
secret_key_file = user_data_dir / '.secret_key'
|
|
16
|
+
|
|
17
|
+
user_data_dir.mkdir(parents=True, exist_ok=True)
|
|
18
|
+
secret_key_file.write_text(secret_key, encoding='utf-8')
|
|
19
|
+
|
|
20
|
+
self.stdout.write(self.style.SUCCESS(
|
|
21
|
+
f'Nova SECRET_KEY gerada e salva com sucesso em: {secret_key_file}'
|
|
22
|
+
))
|
|
23
|
+
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# Generated by Django 4.0.5 on 2024-07-09 23:19
|
|
2
|
+
|
|
3
|
+
from django.db import migrations, models
|
|
4
|
+
import django.db.models.deletion
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class Migration(migrations.Migration):
|
|
8
|
+
|
|
9
|
+
initial = True
|
|
10
|
+
|
|
11
|
+
dependencies = [
|
|
12
|
+
]
|
|
13
|
+
|
|
14
|
+
operations = [
|
|
15
|
+
migrations.CreateModel(
|
|
16
|
+
name='Type',
|
|
17
|
+
fields=[
|
|
18
|
+
('id', models.BigAutoField(auto_created=True,
|
|
19
|
+
primary_key=True, serialize=False, verbose_name='ID')),
|
|
20
|
+
('name', models.CharField(max_length=100)),
|
|
21
|
+
('dependencies', models.JSONField(blank=True, null=True)),
|
|
22
|
+
('created_at', models.DateTimeField(auto_now_add=True)),
|
|
23
|
+
('updated_at', models.DateTimeField(auto_now=True)),
|
|
24
|
+
],
|
|
25
|
+
),
|
|
26
|
+
migrations.CreateModel(
|
|
27
|
+
name='Server',
|
|
28
|
+
fields=[
|
|
29
|
+
('id', models.BigAutoField(auto_created=True,
|
|
30
|
+
primary_key=True, serialize=False, verbose_name='ID')),
|
|
31
|
+
('name', models.CharField(max_length=100)),
|
|
32
|
+
('jar_template', models.CharField(choices=[('custom.jar', 'custom.jar'), (
|
|
33
|
+
'minecraft_server.1.7.10.jar', 'minecraft_server.1.7.10.jar')], max_length=100)),
|
|
34
|
+
('jar', models.CharField(blank=True, max_length=100, null=True)),
|
|
35
|
+
('ip', models.GenericIPAddressField()),
|
|
36
|
+
('port', models.IntegerField()),
|
|
37
|
+
('memory_limit', models.IntegerField(
|
|
38
|
+
default=1024, verbose_name='Memory Limit (MB)')),
|
|
39
|
+
('created_at', models.DateTimeField(auto_now_add=True)),
|
|
40
|
+
('updated_at', models.DateTimeField(auto_now=True)),
|
|
41
|
+
('type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE,
|
|
42
|
+
related_name='servers', to='console.type')),
|
|
43
|
+
],
|
|
44
|
+
),
|
|
45
|
+
]
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Generated by Django 4.0.5 on 2024-07-09 23:49
|
|
2
|
+
|
|
3
|
+
from django.db import migrations, models
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Migration(migrations.Migration):
|
|
7
|
+
|
|
8
|
+
dependencies = [
|
|
9
|
+
('console', '0001_initial'),
|
|
10
|
+
]
|
|
11
|
+
|
|
12
|
+
operations = [
|
|
13
|
+
migrations.AlterField(
|
|
14
|
+
model_name='server',
|
|
15
|
+
name='ip',
|
|
16
|
+
field=models.GenericIPAddressField(blank=True, null=True),
|
|
17
|
+
),
|
|
18
|
+
migrations.AlterField(
|
|
19
|
+
model_name='server',
|
|
20
|
+
name='port',
|
|
21
|
+
field=models.IntegerField(default=25565),
|
|
22
|
+
),
|
|
23
|
+
]
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Generated by Django 4.0.5 on 2024-07-10 00:44
|
|
2
|
+
|
|
3
|
+
from django.db import migrations, models
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Migration(migrations.Migration):
|
|
7
|
+
|
|
8
|
+
dependencies = [
|
|
9
|
+
('console', '0002_alter_server_ip_alter_server_port'),
|
|
10
|
+
]
|
|
11
|
+
|
|
12
|
+
operations = [
|
|
13
|
+
migrations.RemoveField(
|
|
14
|
+
model_name='server',
|
|
15
|
+
name='ip',
|
|
16
|
+
),
|
|
17
|
+
migrations.AddField(
|
|
18
|
+
model_name='server',
|
|
19
|
+
name='status',
|
|
20
|
+
field=models.BooleanField(default=False),
|
|
21
|
+
),
|
|
22
|
+
]
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# Generated by Django 4.0.5 on 2024-07-10 20:29
|
|
2
|
+
|
|
3
|
+
from django.db import migrations, models
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Migration(migrations.Migration):
|
|
7
|
+
|
|
8
|
+
dependencies = [
|
|
9
|
+
('console', '0003_remove_server_ip_server_status'),
|
|
10
|
+
]
|
|
11
|
+
|
|
12
|
+
operations = [
|
|
13
|
+
migrations.AddField(
|
|
14
|
+
model_name='server',
|
|
15
|
+
name='server_properties',
|
|
16
|
+
field=models.TextField(blank=True, default='#Minecraft server properties\n#Wed Jul 10 19:30:06 UTC 2024\ngenerator-settings=\nop-permission-level=4\nallow-nether=true\nlevel-name=world\nenable-query=false\nallow-flight=true\nannounce-player-achievements=true\nserver-port=25566\nlevel-type=DEFAULT\nenable-rcon=false\nforce-gamemode=false\nlevel-seed=\nserver-ip=\nmax-build-height=256\nspawn-npcs=true\nwhite-list=false\nspawn-animals=true\nhardcore=false\nsnooper-enabled=true\nonline-mode=false\nresource-pack=\npvp=true\ndifficulty=1\nenable-command-block=false\ngamemode=0\nplayer-idle-timeout=0\nmax-players=5\nspawn-monsters=true\ngenerate-structures=true\nview-distance=10\nmotd=A Minecraft Server\n', null=True),
|
|
17
|
+
),
|
|
18
|
+
]
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Generated by Django 5.1.15 on 2026-07-17 13:23
|
|
2
|
+
|
|
3
|
+
from django.db import migrations, models
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Migration(migrations.Migration):
|
|
7
|
+
|
|
8
|
+
dependencies = [
|
|
9
|
+
('console', '0004_server_server_properties'),
|
|
10
|
+
]
|
|
11
|
+
|
|
12
|
+
operations = [
|
|
13
|
+
migrations.AlterField(
|
|
14
|
+
model_name='server',
|
|
15
|
+
name='jar_template',
|
|
16
|
+
field=models.CharField(max_length=100),
|
|
17
|
+
),
|
|
18
|
+
migrations.AlterField(
|
|
19
|
+
model_name='server',
|
|
20
|
+
name='server_properties',
|
|
21
|
+
field=models.TextField(blank=True, null=True),
|
|
22
|
+
),
|
|
23
|
+
]
|