diff --git a/apiserver/plane/license/api/views/__init__.py b/apiserver/plane/license/api/views/__init__.py index 6bc69fcc3..4803ed373 100644 --- a/apiserver/plane/license/api/views/__init__.py +++ b/apiserver/plane/license/api/views/__init__.py @@ -1,3 +1 @@ -from .product import ProductEndpoint -from .checkout import CheckoutEndpoint from .instance import InstanceEndpoint \ No newline at end of file diff --git a/apiserver/plane/license/api/views/checkout.py b/apiserver/plane/license/api/views/checkout.py deleted file mode 100644 index 8ac3e2e53..000000000 --- a/apiserver/plane/license/api/views/checkout.py +++ /dev/null @@ -1,71 +0,0 @@ -# Python imports -import os -import json -import requests - -# Django imports -from django.conf import settings - -# Third party imports -from rest_framework import status -from rest_framework.response import Response - -# Module imports -from plane.api.views.base import BaseAPIView -from plane.db.models import Workspace, WorkspaceMember -from plane.license.models import License - -class CheckoutEndpoint(BaseAPIView): - - def post(self, request, slug): - LICENSE_ENGINE_BASE_URL = os.environ.get("LICENSE_ENGINE_BASE_URL", "") - - license = License.objects.first() - - if license is None: - return Response({"error": "Instance is not activated"}, status=status.HTTP_400_BAD_REQUEST) - - - price_id = request.data.get("price_id", False) - - if not price_id : - return Response( - {"error": "Price ID is required"}, - status=status.HTTP_400_BAD_REQUEST, - ) - - workspace = Workspace.objects.get(slug=slug) - total_workspace_members = WorkspaceMember.objects.filter(workspace__slug=slug).count() - - payload = { - "user": { - "id": str(request.user.id), - "first_name": request.user.first_name, - "last_name": request.user.last_name, - "email": request.user.email, - }, - "workspace": { - "id": str(workspace.id), - "name": str(workspace.name), - "slug": str(slug), - }, - "priceId": price_id, - "seats": total_workspace_members, - "return_url": settings.WEB_URL, - } - - headers = { - "Content-Type": "application/json", - "X-Api-Key": str(license.api_key), - } - - response = requests.post( - f"{LICENSE_ENGINE_BASE_URL}/api/checkout/create-session", - data=json.dumps(payload), - headers=headers, - ) - - if response.status_code == 200: - return Response(response.json(), status=status.HTTP_200_OK) - - return Response({"error": "Unable to create a checkout try again later"}, status=response.status_code) diff --git a/apiserver/plane/license/api/views/instance.py b/apiserver/plane/license/api/views/instance.py index a34181539..22f68370a 100644 --- a/apiserver/plane/license/api/views/instance.py +++ b/apiserver/plane/license/api/views/instance.py @@ -2,6 +2,10 @@ import os import json import requests +import uuid + +# Django imports +from django.utils import timezone # Third party imports from rest_framework import status @@ -10,7 +14,8 @@ from rest_framework.permissions import AllowAny # Module imports from plane.api.views import BaseAPIView -from plane.license.models import License +from plane.license.models import Instance +from plane.db.models import User class InstanceEndpoint(BaseAPIView): @@ -20,17 +25,35 @@ class InstanceEndpoint(BaseAPIView): def post(self, request): email = request.data.get("email", False) + password = request.data.get("password", False) with open("package.json", "r") as file: # Load JSON content from the file data = json.load(file) - if not email: + if not email or not password: return Response( - {"error": "Email is required"}, + {"error": "Email and Password are required"}, status=status.HTTP_400_BAD_REQUEST, ) + user = User.objects.filter(email=email).first() + if user is None: + user = User.objects.create( + email=email, + username=uuid.uuid4().hex, + ) + user.set_password(password) + user.save() + else: + if not user.check_password(password): + return Response( + { + "error": "Sorry, we could not find a user with the provided credentials. Please try again." + }, + status=status.HTTP_401_UNAUTHORIZED, + ) + LICENSE_ENGINE_BASE_URL = os.environ.get("LICENSE_ENGINE_BASE_URL", "") headers = {"Content-Type": "application/json"} @@ -45,16 +68,18 @@ class InstanceEndpoint(BaseAPIView): if response.status_code == 201: data = response.json() - license = License.objects.create( + instance = Instance.objects.create( instance_id=data.get("id"), license_key=data.get("license_key"), api_key=data.get("api_key"), version=data.get("version"), email=data.get("email"), + user=user, + last_checked_at=timezone.now(), ) return Response( { - "id": str(license.instance_id), + "id": str(instance.instance_id), "message": "Instance registered succesfully", }, status=status.HTTP_200_OK, @@ -65,14 +90,14 @@ class InstanceEndpoint(BaseAPIView): ) def get(self, request): - license = License.objects.first() + instance = Instance.objects.first() - if license is None: - return Response({"activated": False}, status=status.HTTP_200_OK) + if instance is None: + return Response({"activated": False}, status=status.HTTP_400_BAD_REQUEST) data = { - "instance_id": license.instance_id, - "version": license.version, + "instance_id": instance.instance_id, + "version": instance.version, "activated": True, } diff --git a/apiserver/plane/license/api/views/product.py b/apiserver/plane/license/api/views/product.py deleted file mode 100644 index b8e37d839..000000000 --- a/apiserver/plane/license/api/views/product.py +++ /dev/null @@ -1,38 +0,0 @@ -# Python imports -import os -import requests - -# Third party imports -from rest_framework import status -from rest_framework.response import Response - -# Module imports -from plane.api.views.base import BaseAPIView -from plane.license.models import License - - -class ProductEndpoint(BaseAPIView): - def get(self, request, slug): - LICENSE_ENGINE_BASE_URL = os.environ.get("LICENSE_ENGINE_BASE_URL", "") - - license = License.objects.first() - - if license is None: - return Response( - {"error": "Instance is not activated"}, - status=status.HTTP_400_BAD_REQUEST, - ) - - # # Request the licensing engine - - response = requests.get( - f"{LICENSE_ENGINE_BASE_URL}/api/products", - headers={ - "X-Api-Key": license.api_key, - }, - ) - if response.status_code == 200: - return Response(response.json(), status=status.HTTP_200_OK) - return Response( - {"error": "Unable to fetch products"}, status=response.status_code - ) diff --git a/apiserver/plane/license/migrations/0001_initial.py b/apiserver/plane/license/migrations/0001_initial.py index 8b9765ce2..0a4fdc191 100644 --- a/apiserver/plane/license/migrations/0001_initial.py +++ b/apiserver/plane/license/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 4.2.3 on 2023-10-26 14:14 +# Generated by Django 4.2.3 on 2023-10-30 11:26 from django.conf import settings from django.db import migrations, models @@ -16,22 +16,24 @@ class Migration(migrations.Migration): operations = [ migrations.CreateModel( - name='License', + name='Instance', fields=[ ('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Created At')), ('updated_at', models.DateTimeField(auto_now=True, verbose_name='Last Modified At')), ('id', models.UUIDField(db_index=True, default=uuid.uuid4, editable=False, primary_key=True, serialize=False, unique=True)), - ('instance_id', models.CharField(max_length=12, unique=True)), - ('license_key', models.CharField(max_length=64)), + ('instance_id', models.CharField(max_length=25, unique=True)), + ('license_key', models.CharField(max_length=256)), ('api_key', models.CharField(max_length=16)), ('version', models.CharField(max_length=10)), + ('email', models.CharField(max_length=256)), ('created_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_created_by', to=settings.AUTH_USER_MODEL, verbose_name='Created By')), ('updated_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_updated_by', to=settings.AUTH_USER_MODEL, verbose_name='Last Modified By')), + ('user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='instance_owner', to=settings.AUTH_USER_MODEL)), ], options={ - 'verbose_name': 'License', - 'verbose_name_plural': 'Licenses', - 'db_table': 'licenses', + 'verbose_name': 'Instance', + 'verbose_name_plural': 'Instances', + 'db_table': 'instances', 'ordering': ('-created_at',), }, ), diff --git a/apiserver/plane/license/migrations/0002_license_email.py b/apiserver/plane/license/migrations/0002_license_email.py deleted file mode 100644 index d8163c6d3..000000000 --- a/apiserver/plane/license/migrations/0002_license_email.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 4.2.3 on 2023-10-26 15:22 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('license', '0001_initial'), - ] - - operations = [ - migrations.AddField( - model_name='license', - name='email', - field=models.CharField(default='email@plane.so', max_length=256), - preserve_default=False, - ), - ] diff --git a/apiserver/plane/license/migrations/0003_alter_license_license_key.py b/apiserver/plane/license/migrations/0003_alter_license_license_key.py deleted file mode 100644 index d4fcca54b..000000000 --- a/apiserver/plane/license/migrations/0003_alter_license_license_key.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 4.2.3 on 2023-10-26 15:24 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('license', '0002_license_email'), - ] - - operations = [ - migrations.AlterField( - model_name='license', - name='license_key', - field=models.CharField(max_length=256), - ), - ] diff --git a/apiserver/plane/license/migrations/0004_alter_license_instance_id.py b/apiserver/plane/license/migrations/0004_alter_license_instance_id.py deleted file mode 100644 index a211468b3..000000000 --- a/apiserver/plane/license/migrations/0004_alter_license_instance_id.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 4.2.3 on 2023-10-26 15:26 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('license', '0003_alter_license_license_key'), - ] - - operations = [ - migrations.AlterField( - model_name='license', - name='instance_id', - field=models.CharField(max_length=25, unique=True), - ), - ] diff --git a/apiserver/plane/license/models/__init__.py b/apiserver/plane/license/models/__init__.py index 496931e0c..fe1f8009d 100644 --- a/apiserver/plane/license/models/__init__.py +++ b/apiserver/plane/license/models/__init__.py @@ -1 +1 @@ -from .license import License \ No newline at end of file +from .instance import Instance \ No newline at end of file diff --git a/apiserver/plane/license/models/license.py b/apiserver/plane/license/models/instance.py similarity index 52% rename from apiserver/plane/license/models/license.py rename to apiserver/plane/license/models/instance.py index 95268d1c0..4ce83a4a0 100644 --- a/apiserver/plane/license/models/license.py +++ b/apiserver/plane/license/models/instance.py @@ -1,19 +1,27 @@ # Django imports from django.db import models +from django.conf import settings # Module imports from plane.db.models import BaseModel -class License(BaseModel): +class Instance(BaseModel): instance_id = models.CharField(max_length=25, unique=True) license_key = models.CharField(max_length=256) api_key = models.CharField(max_length=16) version = models.CharField(max_length=10) email = models.CharField(max_length=256) + user = models.ForeignKey( + settings.AUTH_USER_MODEL, + on_delete=models.SET_NULL, + null=True, + related_name="instance_owner", + ) + last_checked_at = models.DateTimeField() class Meta: - verbose_name = "License" - verbose_name_plural = "Licenses" - db_table = "licenses" + verbose_name = "Instance" + verbose_name_plural = "Instances" + db_table = "instances" ordering = ("-created_at",) diff --git a/apiserver/plane/license/urls.py b/apiserver/plane/license/urls.py index 0e83ba268..012f090ae 100644 --- a/apiserver/plane/license/urls.py +++ b/apiserver/plane/license/urls.py @@ -1,18 +1,8 @@ from django.urls import path -from plane.license.api.views import ProductEndpoint, CheckoutEndpoint, InstanceEndpoint +from plane.license.api.views import InstanceEndpoint urlpatterns = [ - path( - "workspaces//products/", - ProductEndpoint.as_view(), - name="products", - ), - path( - "workspaces//create-checkout-session/", - CheckoutEndpoint.as_view(), - name="checkout", - ), path( "instances/", InstanceEndpoint.as_view(),