From 3509acedf9d27ac6975bc08802764093042591ee Mon Sep 17 00:00:00 2001 From: Gilles <43683714+corp-0@users.noreply.github.com> Date: Wed, 19 Jun 2024 06:28:48 -0400 Subject: [PATCH] feat: wip definition of models for new server management --- src/server/__init__.py | 0 src/server/admin.py | 37 +++++ src/server/api/__init__.py | 0 src/server/api/urls.py | 1 + src/server/api/views.py | 0 src/server/apps.py | 6 + src/server/management/__init__.py | 0 src/server/management/commands/__init__.py | 0 .../commands/create_moderation_info.py | 13 ++ src/server/migrations/0001_initial.py | 59 +++++++ src/server/migrations/__init__.py | 0 src/server/models.py | 150 ++++++++++++++++++ 12 files changed, 266 insertions(+) create mode 100644 src/server/__init__.py create mode 100644 src/server/admin.py create mode 100644 src/server/api/__init__.py create mode 100644 src/server/api/urls.py create mode 100644 src/server/api/views.py create mode 100644 src/server/apps.py create mode 100644 src/server/management/__init__.py create mode 100644 src/server/management/commands/__init__.py create mode 100644 src/server/management/commands/create_moderation_info.py create mode 100644 src/server/migrations/0001_initial.py create mode 100644 src/server/migrations/__init__.py create mode 100644 src/server/models.py diff --git a/src/server/__init__.py b/src/server/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/server/admin.py b/src/server/admin.py new file mode 100644 index 0000000..e0d7b6f --- /dev/null +++ b/src/server/admin.py @@ -0,0 +1,37 @@ +from django.contrib import admin + +from .models import ( + AccountModerationInfo, + CodeScanInformation, + ServerAdmonition, + ServerInformation, +) + + +class ServerAdmonitionInline(admin.TabularInline): + model = ServerAdmonition + extra = 1 + raw_id_fields = ["server"] + fields = ["server", "created_at", "reason", "severity"] + readonly_fields = ["created_at"] + + +@admin.register(CodeScanInformation) +class CodeScanInformationAdmin(admin.ModelAdmin): + list_display = ["version"] + + +@admin.register(ServerInformation) +class ServerInformationAdmin(admin.ModelAdmin): + list_display = ["name", "owner", "is_18_plus", "is_delisted"] + search_fields = ["name", "owner__username"] + list_filter = ["is_18_plus", "is_delisted"] + raw_id_fields = ["owner", "code_scan_version"] + + +@admin.register(AccountModerationInfo) +class AccountModerationInfoAdmin(admin.ModelAdmin): + list_display = ["account", "can_create_servers", "can_list_servers"] + search_fields = ["account__username"] + list_filter = ["can_create_servers", "can_list_servers"] + raw_id_fields = ["account"] diff --git a/src/server/api/__init__.py b/src/server/api/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/server/api/urls.py b/src/server/api/urls.py new file mode 100644 index 0000000..ae94233 --- /dev/null +++ b/src/server/api/urls.py @@ -0,0 +1 @@ +app_name = "server" diff --git a/src/server/api/views.py b/src/server/api/views.py new file mode 100644 index 0000000..e69de29 diff --git a/src/server/apps.py b/src/server/apps.py new file mode 100644 index 0000000..40baeb8 --- /dev/null +++ b/src/server/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class ServerConfig(AppConfig): + default_auto_field = "django.db.models.BigAutoField" + name = "server" diff --git a/src/server/management/__init__.py b/src/server/management/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/server/management/commands/__init__.py b/src/server/management/commands/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/server/management/commands/create_moderation_info.py b/src/server/management/commands/create_moderation_info.py new file mode 100644 index 0000000..4b8f1eb --- /dev/null +++ b/src/server/management/commands/create_moderation_info.py @@ -0,0 +1,13 @@ +from django.core.management.base import BaseCommand + +from accounts.models import Account +from server.models import AccountModerationInfo + + +class Command(BaseCommand): + help = "creates AccountModerationInfo for existing accounts" + + def handle(self, *args, **options): + for account in Account.objects.all(): + AccountModerationInfo.objects.get_or_create(account=account) + self.stdout.write(self.style.SUCCESS("Successfully created AccountModerationInfo for all accounts")) diff --git a/src/server/migrations/0001_initial.py b/src/server/migrations/0001_initial.py new file mode 100644 index 0000000..ef79813 --- /dev/null +++ b/src/server/migrations/0001_initial.py @@ -0,0 +1,59 @@ +# Generated by Django 3.2.24 on 2024-06-19 10:17 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name='AccountModerationInfo', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('can_create_servers', models.BooleanField(default=True)), + ('can_list_servers', models.BooleanField(default=True)), + ('account', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='moderation_info', to=settings.AUTH_USER_MODEL)), + ], + ), + migrations.CreateModel( + name='CodeScanInformation', + fields=[ + ('version', models.TextField(primary_key=True, serialize=False)), + ], + ), + migrations.CreateModel( + name='ServerInformation', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.TextField(help_text='Name this server uses to present itself in the servers list', max_length=50, verbose_name='Name')), + ('description', models.TextField(help_text='A brief description of what this server is about', max_length=200, verbose_name='Description')), + ('icon', models.URLField(blank=True, help_text='URL of the image this server uses to present itself in the servers list', verbose_name='Icon')), + ('rules', models.TextField(blank=True, help_text='The rules that players must follow on this server', verbose_name='Rules')), + ('motd', models.TextField(help_text='Message displayed to players when they join the server', verbose_name='Message of the Day (MOTD)')), + ('is_18_plus', models.BooleanField(default=False, help_text='Indicates if this server is intended for players aged 18 and above', verbose_name='18+')), + ('is_delisted', models.BooleanField(help_text='Indicates if this server is delisted from the servers list', verbose_name='Is Delisted')), + ('listing_key', models.TextField(blank=True, help_text='A unique key used for listing this server. Do not lose this key!', null=True, unique=True, verbose_name='Listing Key')), + ('code_scan_version', models.ForeignKey(help_text='What version should this build of the game be tested against in CodeScan', on_delete=django.db.models.deletion.PROTECT, to='server.codescaninformation', verbose_name='CodeScan Version')), + ('owner', models.ForeignKey(help_text='Who created and/or is responsible for this server', on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='Owner')), + ], + ), + migrations.CreateModel( + name='ServerAdmonition', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('created_at', models.DateTimeField(auto_now_add=True, help_text='When was this admonition created', verbose_name='Created at')), + ('reason', models.TextField(help_text='Why was this server warned?', verbose_name='Reason')), + ('severity', models.CharField(choices=[('low', 'Low'), ('medium', 'Medium'), ('high', 'High')], default='low', help_text='The severity level of the admonition', max_length=6)), + ('owner_moderation', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='admonitions', to='server.accountmoderationinfo')), + ('server', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='admonitions', to='server.serverinformation')), + ], + ), + ] diff --git a/src/server/migrations/__init__.py b/src/server/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/server/models.py b/src/server/models.py new file mode 100644 index 0000000..7a9e198 --- /dev/null +++ b/src/server/models.py @@ -0,0 +1,150 @@ +import secrets +import string + +from django.db import models +from django.db.models.signals import pre_save +from django.dispatch import receiver + +from accounts.models import Account + + +class CodeScanInformation(models.Model): + version = models.TextField(primary_key=True) + + def __str__(self): + return self.version + + +def generate_listing_key(): + alphabet = string.ascii_letters + string.digits + "-_" + return "".join(secrets.choice(alphabet) for _ in range(30)) + + +class ServerInformation(models.Model): + 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( + verbose_name="Name", + max_length=50, + help_text="Name this server uses to present itself in the servers list", + ) + description = models.TextField( + verbose_name="Description", + max_length=200, + help_text="A brief description of what this server is about", + ) + icon = models.URLField( + verbose_name="Icon", + blank=True, + help_text="URL of the image this server uses to present itself in the servers list", + ) + rules = models.TextField( + verbose_name="Rules", + blank=True, + help_text="The rules that players must follow on this server", + ) + motd = models.TextField( + verbose_name="Message of the Day (MOTD)", + help_text="Message displayed to players when they join the server", + ) + is_18_plus = models.BooleanField( + verbose_name="18+", + default=False, + help_text="Indicates if this server is intended for players aged 18 and above", + ) + code_scan_version = models.ForeignKey( + verbose_name="CodeScan Version", + to=CodeScanInformation, + on_delete=models.PROTECT, + help_text="What version should this build of the game be tested against in CodeScan", + ) + is_delisted = models.BooleanField( + verbose_name="Is Delisted", + help_text="Indicates if this server is delisted from the servers list", + ) + listing_key = models.TextField( + unique=True, + verbose_name="Listing Key", + null=True, + blank=True, + help_text="A unique key used for listing this server. Do not lose this key!", + ) + + def __str__(self): + return f"Server: {self.name} by: {self.owner.unique_identifier}" + + +@receiver(pre_save, sender=ServerInformation) +def set_listing_key(sender, instance: ServerInformation, **kwargs): + if not instance.listing_key: + unique_key_found = False + while not unique_key_found: + new_key = generate_listing_key() + if not ServerInformation.objects.filter(listing_key=new_key).exists(): + instance.listing_key = new_key + 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 AccountModerationInfo(models.Model): + 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) + + def __str__(self): + return f"Moderation for {self.account.unique_identifier}" + + +class ServerAdmonition(models.Model): + SEVERITY_LEVELS = [ + ("low", "Low"), + ("medium", "Medium"), + ("high", "High"), + ] + + owner_moderation = models.ForeignKey(AccountModerationInfo, related_name="admonitions", on_delete=models.CASCADE) + server = models.ForeignKey( + ServerInformation, + related_name="admonitions", + on_delete=models.SET_NULL, + null=True, + ) + created_at = models.DateTimeField( + verbose_name="Created at", + auto_now_add=True, + help_text="When was this admonition created", + ) + reason = models.TextField(verbose_name="Reason", help_text="Why was this server warned?") + severity = models.CharField( + max_length=6, + choices=SEVERITY_LEVELS, + default="low", + help_text="The severity level of the admonition", + ) + + def __str__(self): + return f"[{self.get_severity_display()}]: {self.reason}"