feat: enhance GameServer installation flow
Improved the game server installation process by implementing several key changes. Added support for optional installation paths in the GameServer model, form, and installation view. This allows users to leave the installation path empty and use a default path, improving usability and flexibility. Additionally, updated the form validation to accept empty paths and provided a user-friendly widget placeholder in the form to guide users. Included a new migration to reflect the optional installation path in the database schema. Updated the installation view to automatically determine the installation path based on settings if not specified by the user. Enhanced the script download process in the installation view by setting a custom User-Agent header. This addresses potential issues with servers that block the default Python User-Agent. Added CSRF_TRUSTED_ORIGINS settings dynamically based on ALLOWED_HOSTS to enhance security. Also updated the .gitignore to exclude the newly introduced default game servers directory from version control. These changes collectively streamline the setup process for new game servers, provide clearer guidance to users, and ensure compatibility with a broader range of hosting configurations.
This commit is contained in:
parent
6778c2a2fb
commit
ce520b914a
6 changed files with 90 additions and 43 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -3,3 +3,4 @@ venv/
|
|||
__pycache__/
|
||||
settings.ini
|
||||
db.sqlite3
|
||||
game_servers/
|
||||
|
|
|
@ -20,10 +20,15 @@ class GameServerForm(forms.ModelForm):
|
|||
"script_file",
|
||||
"script_content",
|
||||
]
|
||||
widgets = {
|
||||
"installation_path": forms.TextInput(
|
||||
attrs={"placeholder": "Leave empty to use default path"}
|
||||
),
|
||||
}
|
||||
|
||||
def clean_installation_path(self):
|
||||
path = self.cleaned_data["installation_path"]
|
||||
if not os.path.isabs(path):
|
||||
path = self.cleaned_data.get("installation_path")
|
||||
if path and not os.path.isabs(path):
|
||||
raise forms.ValidationError(
|
||||
"The installation path must be an absolute path."
|
||||
)
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
# Generated by Django 5.0.6 on 2024-06-05 15:50
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("servers", "0001_initial"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name="gameserver",
|
||||
name="installation_path",
|
||||
field=models.CharField(blank=True, max_length=255, null=True),
|
||||
),
|
||||
]
|
|
@ -12,7 +12,7 @@ class GameServer(models.Model):
|
|||
name = models.CharField(max_length=100)
|
||||
game = models.CharField(max_length=100)
|
||||
status = models.CharField(max_length=100, choices=STATUS_CHOICES, default='stopped')
|
||||
installation_path = models.CharField(max_length=255)
|
||||
installation_path = models.CharField(max_length=255, blank=True, null=True)
|
||||
custom_script_content = models.TextField(blank=True, null=True)
|
||||
|
||||
def __str__(self):
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
# servers/views.py
|
||||
from django.shortcuts import render, redirect
|
||||
from django.views import View
|
||||
from django.conf import settings
|
||||
from .models import GameServer
|
||||
from .forms import GameServerForm
|
||||
from pathlib import Path
|
||||
import subprocess
|
||||
import os
|
||||
import urllib.request
|
||||
|
@ -48,6 +49,10 @@ class InstallServerView(View):
|
|||
form = self.form_class(request.POST, request.FILES)
|
||||
if form.is_valid():
|
||||
server = form.save(commit=False)
|
||||
if not form.cleaned_data["installation_path"]:
|
||||
server.installation_path = os.path.join(
|
||||
settings.GAME_SERVERS_DIR, server.game
|
||||
)
|
||||
os.makedirs(server.installation_path, exist_ok=True)
|
||||
|
||||
if form.cleaned_data["existing_server"]:
|
||||
|
@ -59,6 +64,7 @@ class InstallServerView(View):
|
|||
script_path = self.handle_custom_script(form, server)
|
||||
if not script_path:
|
||||
script_path = self.download_lgsm_script(server.installation_path)
|
||||
print(server.installation_path)
|
||||
server.status = "installing"
|
||||
server.save()
|
||||
subprocess.run(
|
||||
|
@ -101,7 +107,14 @@ class InstallServerView(View):
|
|||
def download_lgsm_script(self, installation_path):
|
||||
url = "https://linuxgsm.sh"
|
||||
script_path = os.path.join(installation_path, "linuxgsm.sh")
|
||||
urllib.request.urlretrieve(url, script_path)
|
||||
request = urllib.request.Request(url)
|
||||
request.add_header(
|
||||
"User-Agent",
|
||||
"Mozilla/5.0 (X11; Linux x86_64; rv:126.0) Gecko/20100101 Firefox/126.0",
|
||||
)
|
||||
response = urllib.request.urlopen(request)
|
||||
with open(script_path, "wb") as script_file:
|
||||
script_file.write(response.read())
|
||||
os.chmod(script_path, 0o755) # Make the script executable
|
||||
return script_path
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@ from autosecretkey import AutoSecretKey
|
|||
# Build paths inside the project like this: BASE_DIR / 'subdir'.
|
||||
BASE_DIR = Path(__file__).resolve().parent.parent
|
||||
|
||||
ASK = AutoSecretKey(BASE_DIR / 'settings.ini')
|
||||
ASK = AutoSecretKey(BASE_DIR / "settings.ini")
|
||||
SECRET_KEY = ASK.secret_key
|
||||
|
||||
CONFIG = ASK.config
|
||||
|
@ -26,59 +26,60 @@ CONFIG = ASK.config
|
|||
DEBUG = CONFIG.getboolean("CoffeeMachine", "Debug", fallback=False)
|
||||
|
||||
ALLOWED_HOSTS = CONFIG.get("CoffeeMachine", "Hostname", fallback="*").split(",")
|
||||
CSRF_TRUSTED_ORIGINS = [f"https://{host}" for host in ALLOWED_HOSTS]
|
||||
|
||||
|
||||
|
||||
# Application definition
|
||||
|
||||
INSTALLED_APPS = [
|
||||
'django.contrib.admin',
|
||||
'django.contrib.auth',
|
||||
'django.contrib.contenttypes',
|
||||
'django.contrib.sessions',
|
||||
'django.contrib.messages',
|
||||
'django.contrib.staticfiles',
|
||||
|
||||
'coffeemachine.servers',
|
||||
"django.contrib.admin",
|
||||
"django.contrib.auth",
|
||||
"django.contrib.contenttypes",
|
||||
"django.contrib.sessions",
|
||||
"django.contrib.messages",
|
||||
"django.contrib.staticfiles",
|
||||
"coffeemachine.servers",
|
||||
]
|
||||
|
||||
MIDDLEWARE = [
|
||||
'django.middleware.security.SecurityMiddleware',
|
||||
'django.contrib.sessions.middleware.SessionMiddleware',
|
||||
'django.middleware.common.CommonMiddleware',
|
||||
'django.middleware.csrf.CsrfViewMiddleware',
|
||||
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||
'django.contrib.messages.middleware.MessageMiddleware',
|
||||
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
||||
"django.middleware.security.SecurityMiddleware",
|
||||
"django.contrib.sessions.middleware.SessionMiddleware",
|
||||
"django.middleware.common.CommonMiddleware",
|
||||
"django.middleware.csrf.CsrfViewMiddleware",
|
||||
"django.contrib.auth.middleware.AuthenticationMiddleware",
|
||||
"django.contrib.messages.middleware.MessageMiddleware",
|
||||
"django.middleware.clickjacking.XFrameOptionsMiddleware",
|
||||
]
|
||||
|
||||
ROOT_URLCONF = 'coffeemachine.urls'
|
||||
ROOT_URLCONF = "coffeemachine.urls"
|
||||
|
||||
TEMPLATES = [
|
||||
{
|
||||
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
||||
'DIRS': [],
|
||||
'APP_DIRS': True,
|
||||
'OPTIONS': {
|
||||
'context_processors': [
|
||||
'django.template.context_processors.debug',
|
||||
'django.template.context_processors.request',
|
||||
'django.contrib.auth.context_processors.auth',
|
||||
'django.contrib.messages.context_processors.messages',
|
||||
"BACKEND": "django.template.backends.django.DjangoTemplates",
|
||||
"DIRS": [],
|
||||
"APP_DIRS": True,
|
||||
"OPTIONS": {
|
||||
"context_processors": [
|
||||
"django.template.context_processors.debug",
|
||||
"django.template.context_processors.request",
|
||||
"django.contrib.auth.context_processors.auth",
|
||||
"django.contrib.messages.context_processors.messages",
|
||||
],
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
WSGI_APPLICATION = 'coffeemachine.wsgi.application'
|
||||
WSGI_APPLICATION = "coffeemachine.wsgi.application"
|
||||
|
||||
|
||||
# Database
|
||||
# https://docs.djangoproject.com/en/5.0/ref/settings/#databases
|
||||
|
||||
DATABASES = {
|
||||
'default': {
|
||||
'ENGINE': 'django.db.backends.sqlite3',
|
||||
'NAME': BASE_DIR / 'db.sqlite3',
|
||||
"default": {
|
||||
"ENGINE": "django.db.backends.sqlite3",
|
||||
"NAME": BASE_DIR / "db.sqlite3",
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -88,16 +89,16 @@ DATABASES = {
|
|||
|
||||
AUTH_PASSWORD_VALIDATORS = [
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
|
||||
"NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator",
|
||||
},
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
|
||||
"NAME": "django.contrib.auth.password_validation.MinimumLengthValidator",
|
||||
},
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
|
||||
"NAME": "django.contrib.auth.password_validation.CommonPasswordValidator",
|
||||
},
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
|
||||
"NAME": "django.contrib.auth.password_validation.NumericPasswordValidator",
|
||||
},
|
||||
]
|
||||
|
||||
|
@ -105,9 +106,9 @@ AUTH_PASSWORD_VALIDATORS = [
|
|||
# Internationalization
|
||||
# https://docs.djangoproject.com/en/5.0/topics/i18n/
|
||||
|
||||
LANGUAGE_CODE = 'en-us'
|
||||
LANGUAGE_CODE = "en-us"
|
||||
|
||||
TIME_ZONE = 'UTC'
|
||||
TIME_ZONE = "UTC"
|
||||
|
||||
USE_I18N = True
|
||||
|
||||
|
@ -117,7 +118,7 @@ USE_TZ = True
|
|||
# Static files (CSS, JavaScript, Images)
|
||||
# https://docs.djangoproject.com/en/5.0/howto/static-files/
|
||||
|
||||
STATIC_URL = 'static/'
|
||||
STATIC_URL = "static/"
|
||||
|
||||
STATICFILES_DIRS = [
|
||||
BASE_DIR / "coffeemachine" / "static",
|
||||
|
@ -126,4 +127,13 @@ STATICFILES_DIRS = [
|
|||
# Default primary key field type
|
||||
# https://docs.djangoproject.com/en/5.0/ref/settings/#default-auto-field
|
||||
|
||||
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
|
||||
DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
|
||||
|
||||
# Coffee Machine settings
|
||||
|
||||
GAME_SERVERS_DIR = Path(
|
||||
CONFIG.get("CoffeeMachine", "GameServersDir", fallback=BASE_DIR / "game_servers")
|
||||
)
|
||||
|
||||
if not GAME_SERVERS_DIR.exists():
|
||||
GAME_SERVERS_DIR.mkdir()
|
||||
|
|
Loading…
Reference in a new issue