diff --git a/deployment/admin.py b/deployment/admin.py index 6cae0aa..b528e54 100644 --- a/deployment/admin.py +++ b/deployment/admin.py @@ -1,6 +1,10 @@ from django.contrib import admin -from deployment.models import Deployment +from deployment.models import Deployment, DeploymentUser + + +class DeploymentUserAdmin(admin.ModelAdmin): + pass class DeploymentAdmin(admin.ModelAdmin): @@ -8,3 +12,4 @@ class DeploymentAdmin(admin.ModelAdmin): admin.site.register(Deployment, DeploymentAdmin) +admin.site.register(DeploymentUser, DeploymentUserAdmin) diff --git a/deployment/channel.py b/deployment/channel.py index 7bf9336..fb30ef9 100644 --- a/deployment/channel.py +++ b/deployment/channel.py @@ -9,9 +9,9 @@ def parse_channel(channel: str) -> tuple[str] | None: parts = channel.lstrip("deployment_").split("_") if len_part := len(parts): if len_part == 1: - return (parts[0], "") + return (int(parts[0]), "") if len_part == 2: - return (parts[0], parts[1]) + return (int(parts[0]), parts[1]) return diff --git a/deployment/migrations/0002_deploymentuser.py b/deployment/migrations/0002_deploymentuser.py new file mode 100644 index 0000000..84ffa6a --- /dev/null +++ b/deployment/migrations/0002_deploymentuser.py @@ -0,0 +1,25 @@ +# Generated by Django 4.2.5 on 2023-09-23 14:21 + +from django.conf import settings +import django.core.validators +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('deployment', '0001_initial'), + ] + + operations = [ + migrations.CreateModel( + name='DeploymentUser', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('credits', models.SmallIntegerField(default=1, help_text='number of deployment allowed (-1 infinite)', validators=[django.core.validators.MinValueValidator(-1, message='Value must be greater than or equal to -1')])), + ('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ], + ), + ] diff --git a/deployment/models.py b/deployment/models.py index ec0e511..4a6390e 100644 --- a/deployment/models.py +++ b/deployment/models.py @@ -3,6 +3,7 @@ from typing import Any from django.contrib.auth.models import User +from django.core.validators import MinValueValidator from django.db import models @@ -28,6 +29,21 @@ class Status(Enum): return [(s.name, s.value) for s in cls] +class DeploymentUser(models.Model): + user = models.OneToOneField(User, on_delete=models.CASCADE) + credits = models.SmallIntegerField( + null=False, + default=1, + help_text="number of deployment allowed (-1 infinite)", + validators=[ + MinValueValidator(-1, message="Value must be greater than or equal to -1"), + ], + ) + + def __str__(self) -> str: + return f"{self.user.username} - {self.credits}" + + class Deployment(models.Model): id = models.UUIDField(primary_key=True) name = models.TextField(null=False, blank=False) diff --git a/deployment/views.py b/deployment/views.py index e995f95..228858e 100644 --- a/deployment/views.py +++ b/deployment/views.py @@ -2,11 +2,14 @@ from uuid import uuid4 from celery.result import AsyncResult from celery.contrib.abortable import AbortableAsyncResult +from django.contrib.auth.models import User +from django.core.exceptions import ObjectDoesNotExist from django.core.paginator import Paginator from django.http import ( HttpResponseRedirect, HttpResponseServerError, HttpResponseBadRequest, + HttpResponseForbidden, ) from django.shortcuts import render, get_object_or_404 @@ -16,6 +19,21 @@ from deployment.models import Deployment, Status from deployment.tasks import deploy as launch_deploy +def check_user_credits(user: User) -> bool: + if not user.is_superuser: + try: + dep_user = user.deploymentuser + except (ObjectDoesNotExist, AttributeError): + return False + else: + if dep_user.credits < 0: + return True + + deployments = Deployment.objects.filter(user=user) + return not len(deployments) >= dep_user.credits + return True + + def index(request): deployments = Deployment.objects.filter(user=request.user.id).order_by( "-created_at" @@ -32,6 +50,7 @@ def index(request): "page_obj": page_obj, "range_pages": (i + 1 for i in range(page_obj.paginator.num_pages)), "url": f"events/{request.user.id}/", + "can_create": check_user_credits(request.user), }, ) @@ -109,6 +128,11 @@ def details(request, deployment_id): def create(request): if request.method == "POST": + if not check_user_credits(request.user): + return HttpResponseForbidden( + "unable to launch deployment, contact administrator for support" + ) + form = DeploymentForm(request.POST) if form.is_valid(): try: diff --git a/pyproject.toml b/pyproject.toml index e410359..231a52b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,7 +8,8 @@ exclude = [ ".git", ".ruff_cache", "venv", - "settings.py" + "settings.py", + "deployment/migrations/*.py" ] line-length = 88 diff --git a/templates/deployment/board.html b/templates/deployment/board.html index 61c1c3a..a7e022f 100644 --- a/templates/deployment/board.html +++ b/templates/deployment/board.html @@ -58,9 +58,12 @@ {% endfor %} {% include 'pagination.html' %} - + {% if not can_create %} +
You can't create new deployments, contact administrator for support.
+ {% endif %} {% else %}