diff --git a/panel/src/panel/configuration/__init__.py b/panel/src/panel/configuration/__init__.py new file mode 100644 index 00000000..9104cf94 --- /dev/null +++ b/panel/src/panel/configuration/__init__.py @@ -0,0 +1,31 @@ +import os +import re +import sys + +from importlib import import_module +from importlib.util import find_spec +from django.utils.functional import classproperty + +from django.apps import apps +from django.db import models + + +class Version(): + + model: models.Model + + @classproperty + def latest(cls): + current_dir = os.path.dirname(os.path.abspath(__file__)) + version_pattern = re.compile(r'v(\d+)\.py$') + versions = [] + for filename in os.listdir(current_dir): + match = version_pattern.match(filename) + if match: + versions.append(int(match.group(1))) + + return max(versions) + + def __init__(self, version: int): + module = import_module(f"{__name__}.v{version}") + self.model = getattr(module, "Configuration") diff --git a/panel/src/panel/configuration/v1.py b/panel/src/panel/configuration/v1.py new file mode 100644 index 00000000..f15fc433 --- /dev/null +++ b/panel/src/panel/configuration/v1.py @@ -0,0 +1,19 @@ +from pydantic import BaseModel, Field +from enum import Enum + +class Configuration(BaseModel): + enable: bool = Field( + default=False, + description="Enable the configuration", + ) + + # XXX: hard-code available apex domains for now, + # they will be prefixed by the user name + class Domain(Enum): + EU = "fediversity.eu" + NET = "fediversity.net" + + domain: Domain = Field( + default=Domain.EU, + description="DNS domain where to expose services" + ) diff --git a/panel/src/panel/migrations/0001_initial.py b/panel/src/panel/migrations/0001_initial.py new file mode 100644 index 00000000..12fda70b --- /dev/null +++ b/panel/src/panel/migrations/0001_initial.py @@ -0,0 +1,26 @@ +# Generated by Django 4.2.16 on 2025-03-05 08:25 + +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='Configuration', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('version', models.PositiveIntegerField(default=1, help_text='Configuration schema version')), + ('value', models.JSONField(help_text='Stored configuration value', null=True)), + ('operator', models.ForeignKey(help_text='Operator who owns the configuration', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='configurations', to=settings.AUTH_USER_MODEL)), + ], + ), + ] diff --git a/panel/src/panel/migrations/__init__.py b/panel/src/panel/migrations/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/panel/src/panel/models.py b/panel/src/panel/models.py new file mode 100644 index 00000000..aba43aa0 --- /dev/null +++ b/panel/src/panel/models.py @@ -0,0 +1,29 @@ +from django.db import models +from django.contrib.auth.models import User + +from panel.configuration import Version + +from pydantic import BaseModel + +class Configuration(models.Model): + operator = models.ForeignKey( + User, + on_delete=models.SET_NULL, + null=True, + related_name="configurations", + help_text="Operator who owns the configuration", + ) + + version = models.PositiveIntegerField( + help_text="Configuration schema version", + default=Version.latest, + ) + + value = models.JSONField( + help_text="Stored configuration value", + default=Version(Version.latest).model().model_dump_json, + ) + + @property + def parsed_value(self) -> BaseModel: + return Version(self.version).model.model_validate_json(self.value)