dev: initiate licensing

This commit is contained in:
pablohashescobar 2023-10-30 16:32:51 +05:30
parent 3bfbc3a132
commit 5b2a1dda72
19 changed files with 336 additions and 21 deletions

4
apiserver/package.json Normal file
View File

@ -0,0 +1,4 @@
{
"name": "plane-api",
"version": "0.13.2"
}

View File

@ -1,21 +0,0 @@
# Generated by Django 4.2.5 on 2023-10-18 12:04
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
import plane.db.models.issue
class Migration(migrations.Migration):
dependencies = [
('db', '0045_issueactivity_epoch_workspacemember_issue_props_and_more'),
]
operations = [
migrations.AlterField(
model_name='issueproperty',
name='properties',
field=models.JSONField(default=plane.db.models.issue.get_default_properties),
),
]

View File

View File

View File

@ -0,0 +1,3 @@
from .product import ProductEndpoint
from .checkout import CheckoutEndpoint
from .instance import InstanceEndpoint

View File

@ -0,0 +1,71 @@
# 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)

View File

@ -0,0 +1,79 @@
# Python imports
import os
import json
import requests
# Third party imports
from rest_framework import status
from rest_framework.response import Response
from rest_framework.permissions import AllowAny
# Module imports
from plane.api.views import BaseAPIView
from plane.license.models import License
class InstanceEndpoint(BaseAPIView):
permission_classes = [
AllowAny,
]
def post(self, request):
email = request.data.get("email", False)
with open("package.json", "r") as file:
# Load JSON content from the file
data = json.load(file)
if not email:
return Response(
{"error": "Email is required"},
status=status.HTTP_400_BAD_REQUEST,
)
LICENSE_ENGINE_BASE_URL = os.environ.get("LICENSE_ENGINE_BASE_URL", "")
headers = {"Content-Type": "application/json"}
payload = {"email": email, "version": data.get("version", 0.1)}
response = requests.post(
f"{LICENSE_ENGINE_BASE_URL}/api/instances",
headers=headers,
data=json.dumps(payload),
)
if response.status_code == 201:
data = response.json()
license = License.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"),
)
return Response(
{
"id": str(license.instance_id),
"message": "Instance registered succesfully",
},
status=status.HTTP_200_OK,
)
return Response(
{"error": "Unable to create instance"}, status=response.status_code
)
def get(self, request):
license = License.objects.first()
if license is None:
return Response({"activated": False}, status=status.HTTP_200_OK)
data = {
"instance_id": license.instance_id,
"version": license.version,
"activated": True,
}
return Response(data, status=status.HTTP_200_OK)

View File

@ -0,0 +1,38 @@
# 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
)

View File

@ -0,0 +1,5 @@
from django.apps import AppConfig
class LicenseConfig(AppConfig):
name = "plane.license"

View File

@ -0,0 +1,38 @@
# Generated by Django 4.2.3 on 2023-10-26 14:14
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
import uuid
class Migration(migrations.Migration):
initial = True
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='License',
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)),
('api_key', models.CharField(max_length=16)),
('version', models.CharField(max_length=10)),
('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')),
],
options={
'verbose_name': 'License',
'verbose_name_plural': 'Licenses',
'db_table': 'licenses',
'ordering': ('-created_at',),
},
),
]

View File

@ -0,0 +1,19 @@
# 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,
),
]

View File

@ -0,0 +1,18 @@
# 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),
),
]

View File

@ -0,0 +1,18 @@
# 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),
),
]

View File

@ -0,0 +1 @@
from .license import License

View File

@ -0,0 +1,19 @@
# Django imports
from django.db import models
# Module imports
from plane.db.models import BaseModel
class License(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)
class Meta:
verbose_name = "License"
verbose_name_plural = "Licenses"
db_table = "licenses"
ordering = ("-created_at",)

View File

@ -0,0 +1,21 @@
from django.urls import path
from plane.license.api.views import ProductEndpoint, CheckoutEndpoint, InstanceEndpoint
urlpatterns = [
path(
"workspaces/<str:slug>/products/",
ProductEndpoint.as_view(),
name="products",
),
path(
"workspaces/<str:slug>/create-checkout-session/",
CheckoutEndpoint.as_view(),
name="checkout",
),
path(
"instances/",
InstanceEndpoint.as_view(),
name="instance",
),
]

View File

@ -29,6 +29,7 @@ INSTALLED_APPS = [
"plane.utils", "plane.utils",
"plane.web", "plane.web",
"plane.middleware", "plane.middleware",
"plane.license",
# Third-party things # Third-party things
"rest_framework", "rest_framework",
"rest_framework.authtoken", "rest_framework.authtoken",

View File

@ -14,6 +14,7 @@ urlpatterns = [
# path("admin/", admin.site.urls), # path("admin/", admin.site.urls),
path("", TemplateView.as_view(template_name="index.html")), path("", TemplateView.as_view(template_name="index.html")),
path("api/", include("plane.api.urls")), path("api/", include("plane.api.urls")),
path("api/licenses/", include("plane.license.urls")),
path("", include("plane.web.urls")), path("", include("plane.web.urls")),
] ]