From feed72733955cca6a2993fd6fee7854dc9c24d91 Mon Sep 17 00:00:00 2001 From: Gilles <43683714+corp-0@users.noreply.github.com> Date: Fri, 20 Sep 2024 03:27:17 -0300 Subject: [PATCH] feat: wip definition of models for new server management --- dev-compose.yml | 10 ++--- docker-compose.yml | 6 +-- example.env | 3 +- poetry.lock | 60 ++++++---------------------- pyproject.toml | 2 +- src/central_command/settings.py | 10 ++--- src/server/models.py | 71 +++++++++++++++++++++------------ 7 files changed, 69 insertions(+), 93 deletions(-) diff --git a/dev-compose.yml b/dev-compose.yml index 91b8ced..e98b6e0 100644 --- a/dev-compose.yml +++ b/dev-compose.yml @@ -10,12 +10,10 @@ services: ports: - "5432:5432" - redis: - image: redis:7.2.5-alpine + memcached: + image: memcached:1.6-alpine ports: - - "6379:6379" - environment: - REDIS_PORT: 6379 + - "11211:11211" web: depends_on: @@ -30,5 +28,3 @@ services: volumes: db-data: - static-volume: - media-volume: diff --git a/docker-compose.yml b/docker-compose.yml index 08eeef1..af753e4 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -8,10 +8,8 @@ services: volumes: - db-data:/var/lib/postgresql/data - redis: - image: redis:7.2.5-alpine - environment: - REDIS_PORT: 6379 + memcached: + image: memcached:1.6-alpine web: image: unitystation/central-command:latest diff --git a/example.env b/example.env index 97553ad..922cdc1 100644 --- a/example.env +++ b/example.env @@ -13,7 +13,8 @@ DB_ENGINE=django.db.backends.postgresql DB_NAME=postgres DB_HOST=db DB_PORT=5432 -REDIS_HOST=redis +MEMCACHED_LOCATION=127.0.0.1 +MEMCACHED_PORT=11211 # Links to stuff WEBSITE_URL = http://localhost:8000 diff --git a/poetry.lock b/poetry.lock index 3420c78..520a1b3 100644 --- a/poetry.lock +++ b/poetry.lock @@ -14,17 +14,6 @@ files = [ [package.extras] tests = ["mypy (>=0.800)", "pytest", "pytest-asyncio"] -[[package]] -name = "async-timeout" -version = "4.0.3" -description = "Timeout context manager for asyncio programs" -optional = false -python-versions = ">=3.7" -files = [ - {file = "async-timeout-4.0.3.tar.gz", hash = "sha256:4640d96be84d82d02ed59ea2b7105a0f7b33abe8703703cd0ab0bf87c427522f"}, - {file = "async_timeout-4.0.3-py3-none-any.whl", hash = "sha256:7405140ff1230c310e51dc27b3145b9092d659ce68ff733fb0cefe3ee42be028"}, -] - [[package]] name = "attrs" version = "23.2.0" @@ -353,24 +342,6 @@ pytz = "*" prevent-xss = ["bleach"] test = ["tox (>=2.3)"] -[[package]] -name = "django-redis" -version = "5.4.0" -description = "Full featured redis cache backend for Django." -optional = false -python-versions = ">=3.6" -files = [ - {file = "django-redis-5.4.0.tar.gz", hash = "sha256:6a02abaa34b0fea8bf9b707d2c363ab6adc7409950b2db93602e6cb292818c42"}, - {file = "django_redis-5.4.0-py3-none-any.whl", hash = "sha256:ebc88df7da810732e2af9987f7f426c96204bf89319df4c6da6ca9a2942edd5b"}, -] - -[package.dependencies] -Django = ">=3.2" -redis = ">=3,<4.0.0 || >4.0.0,<4.0.1 || >4.0.1" - -[package.extras] -hiredis = ["redis[hiredis] (>=3,!=4.0.0,!=4.0.1)"] - [[package]] name = "django-rest-knox" version = "4.2.0" @@ -789,6 +760,17 @@ files = [ {file = "pycparser-2.21.tar.gz", hash = "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"}, ] +[[package]] +name = "pymemcache" +version = "4.0.0" +description = "A comprehensive, fast, pure Python memcached client" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pymemcache-4.0.0-py2.py3-none-any.whl", hash = "sha256:f507bc20e0dc8d562f8df9d872107a278df049fa496805c1431b926f3ddd0eab"}, + {file = "pymemcache-4.0.0.tar.gz", hash = "sha256:27bf9bd1bbc1e20f83633208620d56de50f14185055e49504f4f5e94e94aff94"}, +] + [[package]] name = "python-dotenv" version = "0.19.2" @@ -873,24 +855,6 @@ files = [ {file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"}, ] -[[package]] -name = "redis" -version = "5.0.6" -description = "Python client for Redis database and key-value store" -optional = false -python-versions = ">=3.7" -files = [ - {file = "redis-5.0.6-py3-none-any.whl", hash = "sha256:c0d6d990850c627bbf7be01c5c4cbaadf67b48593e913bb71c9819c30df37eee"}, - {file = "redis-5.0.6.tar.gz", hash = "sha256:38473cd7c6389ad3e44a91f4c3eaf6bcb8a9f746007f29bf4fb20824ff0b2197"}, -] - -[package.dependencies] -async-timeout = {version = ">=4.0.3", markers = "python_full_version < \"3.11.3\""} - -[package.extras] -hiredis = ["hiredis (>=1.0.0)"] -ocsp = ["cryptography (>=36.0.1)", "pyopenssl (==20.0.1)", "requests (>=2.26.0)"] - [[package]] name = "referencing" version = "0.33.0" @@ -1245,4 +1209,4 @@ brotli = ["Brotli"] [metadata] lock-version = "2.0" python-versions = "^3.11" -content-hash = "2e0aa71d8af6fe9e0a0208f297a15e749c8b0e760bb65269facc9e1c4f184718" +content-hash = "8c18f99d1f35a62637104a1bdf388486ec3d8dac9784319374f9992366592ce3" diff --git a/pyproject.toml b/pyproject.toml index bd238fc..223a28d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -101,7 +101,7 @@ python-dotenv = "^0.19.2" whitenoise = "^6.2.0" django-post-office = "^3.8.0" drf-spectacular = "^0.27.1" -django-redis = "^5.4.0" +pymemcache = "^4.0.0" [tool.poetry.group.lint.dependencies] pre-commit = "3.*" diff --git a/src/central_command/settings.py b/src/central_command/settings.py index 24fdea7..6c5f6fe 100644 --- a/src/central_command/settings.py +++ b/src/central_command/settings.py @@ -103,15 +103,13 @@ } } -REDIS_LOCATION = f"redis://{os.environ.get('REDIS_HOST')}:6379/1" +MEMCACHED_HOST = os.environ.get("MEMCACHED_HOST", "127.0.0.1") +MEMCACHED_PORT = os.environ.get("MEMCACHED_PORT", 11211) CACHES = { "default": { - "BACKEND": "django_redis.cache.RedisCache", - "LOCATION": REDIS_LOCATION, - "OPTIONS": { - "CLIENT_CLASS": "django_redis.client.DefaultClient", - }, + "BACKEND": "django.core.cache.backends.memcached.PyMemcacheCache", + "LOCATION": f"{MEMCACHED_HOST}:{MEMCACHED_PORT}", } } diff --git a/src/server/models.py b/src/server/models.py index 7a9e198..ff0f865 100644 --- a/src/server/models.py +++ b/src/server/models.py @@ -1,5 +1,4 @@ import secrets -import string from django.db import models from django.db.models.signals import pre_save @@ -9,6 +8,8 @@ class CodeScanInformation(models.Model): + """Information regarding the CodeScan that will be used to scan the code for this build after downloading on clients""" + version = models.TextField(primary_key=True) def __str__(self): @@ -16,23 +17,25 @@ def __str__(self): def generate_listing_key(): - alphabet = string.ascii_letters + string.digits + "-_" - return "".join(secrets.choice(alphabet) for _ in range(30)) + # 22 bytes produce 30 characters + return secrets.token_urlsafe(22) class ServerInformation(models.Model): + """Basic information that describes and identifies a server""" + owner = models.ForeignKey( verbose_name="Owner", to=Account, on_delete=models.CASCADE, help_text="Who created and/or is responsible for this server", ) - name = models.TextField( + name = models.CharField( verbose_name="Name", max_length=50, help_text="Name this server uses to present itself in the servers list", ) - description = models.TextField( + description = models.CharField( verbose_name="Description", max_length=200, help_text="A brief description of what this server is about", @@ -47,9 +50,10 @@ class ServerInformation(models.Model): blank=True, help_text="The rules that players must follow on this server", ) - motd = models.TextField( + motd = models.CharField( verbose_name="Message of the Day (MOTD)", help_text="Message displayed to players when they join the server", + max_length=255, ) is_18_plus = models.BooleanField( verbose_name="18+", @@ -66,11 +70,12 @@ class ServerInformation(models.Model): verbose_name="Is Delisted", help_text="Indicates if this server is delisted from the servers list", ) - listing_key = models.TextField( + listing_key = models.CharField( unique=True, verbose_name="Listing Key", null=True, blank=True, + max_length=30, help_text="A unique key used for listing this server. Do not lose this key!", ) @@ -78,6 +83,7 @@ def __str__(self): return f"Server: {self.name} by: {self.owner.unique_identifier}" +# Binds the save event of ServerInformation model to this function so that the key is generated every time a new ServerInformation is created @receiver(pre_save, sender=ServerInformation) def set_listing_key(sender, instance: ServerInformation, **kwargs): if not instance.listing_key: @@ -89,28 +95,39 @@ def set_listing_key(sender, instance: ServerInformation, **kwargs): unique_key_found = True -# class ServerStatus(models.Model): -# server = models.ForeignKey( -# ServerInformation, related_name="status", on_delete=models.CASCADE -# ) -# is_passworded = models.BooleanField() -# fork_name = models.TextField() -# build_version = models.TextField() -# current_map = models.TextField() -# game_mode = models.TextField() -# ingame_time = models.TextField() -# round_time = models.TextField() -# player_count = models.PositiveSmallIntegerField() -# player_count_max = models.PositiveSmallIntegerField() -# ip = models.TextField() -# port = models.PositiveSmallIntegerField() -# windows_download = models.URLField() -# osx_download = models.URLField() -# linux_download = models.URLField() -# fps = models.PositiveSmallIntegerField() +class ServerTag(models.Model): + """Represents an individual tag a server could have attached to it to make them easier to find by categories""" + + server = models.ForeignKey(to=ServerInformation, related_name="tags", on_delete=models.CASCADE) + name = models.TextField(verbose_name="Name", max_length=50) + + # class ServerStatus(models.Model): + # server = models.ForeignKey( + # ServerInformation, related_name="status", on_delete=models.CASCADE + # ) + # is_passworded = models.BooleanField() + # fork_name = models.TextField() + # build_version = models.TextField() + # current_map = models.TextField() + # game_mode = models.TextField() + # ingame_time = models.TextField() + # round_time = models.TextField() + # player_count = models.PositiveSmallIntegerField() + # player_count_max = models.PositiveSmallIntegerField() + # ip = models.TextField() + # port = models.PositiveSmallIntegerField() + # windows_download = models.URLField() + # osx_download = models.URLField() + # linux_download = models.URLField() + # fps = models.PositiveSmallIntegerField() + + def __str__(self) -> str: + return self.name class AccountModerationInfo(models.Model): + """Information an account has permanently attached to it for the purpose of moderating their behaviour on the hub""" + account = models.OneToOneField(to=Account, related_name="moderation_info", on_delete=models.CASCADE) can_create_servers = models.BooleanField(default=True) can_list_servers = models.BooleanField(default=True) @@ -120,6 +137,8 @@ def __str__(self): class ServerAdmonition(models.Model): + """Represents a warning or reprimand an account received for their misbehaviour or mismanagement of their server""" + SEVERITY_LEVELS = [ ("low", "Low"), ("medium", "Medium"),