forked from github/plane
feat: self hosted licensing initialize
This commit is contained in:
parent
1fde71c633
commit
b3868423f6
@ -1,3 +1 @@
|
||||
from .product import ProductEndpoint
|
||||
from .checkout import CheckoutEndpoint
|
||||
from .instance import InstanceEndpoint
|
@ -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)
|
@ -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,
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
)
|
@ -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',),
|
||||
},
|
||||
),
|
||||
|
@ -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,
|
||||
),
|
||||
]
|
@ -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),
|
||||
),
|
||||
]
|
@ -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),
|
||||
),
|
||||
]
|
@ -1 +1 @@
|
||||
from .license import License
|
||||
from .instance import Instance
|
@ -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",)
|
@ -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/<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(),
|
||||
|
Loading…
Reference in New Issue
Block a user