forked from github/plane
dev: instance configuration on instance bootup
This commit is contained in:
parent
216f07f9d8
commit
4fdd6a0e26
80
apiserver/bin/instance_registration.py
Normal file
80
apiserver/bin/instance_registration.py
Normal file
@ -0,0 +1,80 @@
|
||||
# Python imports
|
||||
import os, sys
|
||||
import json
|
||||
import uuid
|
||||
import requests
|
||||
# Django imports
|
||||
from django.utils import timezone
|
||||
|
||||
# Module imports
|
||||
from plane.db.models import User
|
||||
from plane.license.models import Instance
|
||||
|
||||
sys.path.append("/code")
|
||||
|
||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "plane.settings.production")
|
||||
|
||||
import django
|
||||
|
||||
django.setup()
|
||||
|
||||
|
||||
def instance_registration():
|
||||
# Check if the instance is registered
|
||||
instance = Instance.objects.first()
|
||||
|
||||
# If instance is None then register this instance
|
||||
if instance is None:
|
||||
with open("package.json", "r") as file:
|
||||
# Load JSON content from the file
|
||||
data = json.load(file)
|
||||
|
||||
admin_email = os.environ.get("ADMIN_EMAIL")
|
||||
# Raise an exception if the admin email is not provided
|
||||
if not admin_email:
|
||||
raise Exception("ADMIN_EMAIL is required")
|
||||
|
||||
# Check if the admin email user exists
|
||||
user = User.objects.filter(email=admin_email).first()
|
||||
|
||||
# If the user does not exist create the user and add him to the database
|
||||
if user is None:
|
||||
user = User.objects.create(email=admin_email, username=uuid.uuid4().hex)
|
||||
user.set_password(uuid.uuid4().hex)
|
||||
user.save()
|
||||
|
||||
license_engine_base_url = os.environ.get("LICENSE_ENGINE_BASE_URL")
|
||||
|
||||
if not license_engine_base_url:
|
||||
raise Exception("LICENSE_ENGINE_BASE_URL is required")
|
||||
|
||||
headers = {"Content-Type": "application/json"}
|
||||
|
||||
payload = {
|
||||
"email": user.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()
|
||||
instance = Instance.objects.create(
|
||||
instance_name="Plane Free",
|
||||
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"),
|
||||
owner=user,
|
||||
last_checked_at=timezone.now(),
|
||||
)
|
||||
print("Instance succesfully registered")
|
||||
|
||||
print("Instance could not be registered")
|
||||
else:
|
||||
print(f"Instance already registered with instance owner: {instance.owner.email}")
|
@ -3,6 +3,9 @@ set -e
|
||||
python manage.py wait_for_db
|
||||
python manage.py migrate
|
||||
|
||||
# Register instance
|
||||
python bin/instance_registration.py
|
||||
# Load the configuration variable
|
||||
python bin/instance_configuration.py
|
||||
|
||||
exec gunicorn -w $GUNICORN_WORKERS -k uvicorn.workers.UvicornWorker plane.asgi:application --bind 0.0.0.0:8000 --max-requests 1200 --max-requests-jitter 1000 --access-logfile -
|
||||
|
@ -10,7 +10,6 @@ from django.utils import timezone
|
||||
# Third party imports
|
||||
from rest_framework import status
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.permissions import AllowAny, IsAuthenticated
|
||||
|
||||
# Module imports
|
||||
from plane.api.views import BaseAPIView
|
||||
@ -20,122 +19,29 @@ from plane.db.models import User
|
||||
|
||||
|
||||
class InstanceEndpoint(BaseAPIView):
|
||||
def get_permissions(self):
|
||||
if self.request.method == "PATCH":
|
||||
self.permission_classes = [
|
||||
IsAuthenticated,
|
||||
]
|
||||
else:
|
||||
self.permission_classes = [
|
||||
AllowAny,
|
||||
]
|
||||
|
||||
return super(InstanceEndpoint, self).get_permissions()
|
||||
|
||||
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 or not password:
|
||||
return Response(
|
||||
{"error": "Email and Password are required"},
|
||||
status=status.HTTP_400_BAD_REQUEST,
|
||||
)
|
||||
|
||||
instance = Instance.objects.first()
|
||||
|
||||
if instance is None:
|
||||
# Register the instance
|
||||
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"}
|
||||
|
||||
payload = {
|
||||
"email": email,
|
||||
"version": data.get("version", 0.1),
|
||||
"domain": str(request.headers.get("Host")),
|
||||
}
|
||||
|
||||
response = requests.post(
|
||||
f"{LICENSE_ENGINE_BASE_URL}/api/instances",
|
||||
headers=headers,
|
||||
data=json.dumps(payload),
|
||||
)
|
||||
|
||||
if response.status_code == 201:
|
||||
data = response.json()
|
||||
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(),
|
||||
)
|
||||
|
||||
serializer = InstanceSerializer(instance)
|
||||
|
||||
return Response(
|
||||
{
|
||||
"data": serializer.data,
|
||||
"message": "Instance registered succesfully",
|
||||
},
|
||||
status=status.HTTP_200_OK,
|
||||
)
|
||||
|
||||
return Response(
|
||||
{"error": "Unable to create instance"}, status=response.status_code
|
||||
)
|
||||
|
||||
serializer = InstanceSerializer(instance)
|
||||
|
||||
return Response(
|
||||
{
|
||||
"message": "Instance is already registered",
|
||||
"data": serializer.data,
|
||||
},
|
||||
status=status.HTTP_200_OK,
|
||||
)
|
||||
|
||||
def get(self, request):
|
||||
instance = Instance.objects.first()
|
||||
|
||||
# get the instance
|
||||
if instance is None:
|
||||
return Response({"activated": False}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
# Check the accessing user
|
||||
if str(request.user.id) != str(instance.owner_id):
|
||||
return Response({"error": "Forbidden"}, status=status.HTTP_403_FORBIDDEN)
|
||||
# Return instance
|
||||
serializer = InstanceSerializer(instance)
|
||||
|
||||
data = {
|
||||
"data": serializer.data,
|
||||
"activated": True,
|
||||
}
|
||||
|
||||
return Response(data, status=status.HTTP_200_OK)
|
||||
|
||||
def patch(self, request):
|
||||
# Get the instance
|
||||
instance = Instance.objects.first()
|
||||
# Check the accessing user
|
||||
if instance is not None and str(request.user.id) != str(instance.owner_id):
|
||||
return Response({"error": "Forbidden"}, status=status.HTTP_403_FORBIDDEN)
|
||||
serializer = InstanceSerializer(instance, data=request.data, partial=True)
|
||||
if serializer.is_valid():
|
||||
serializer.save()
|
||||
@ -144,27 +50,32 @@ class InstanceEndpoint(BaseAPIView):
|
||||
|
||||
|
||||
class TransferOwnerEndpoint(BaseAPIView):
|
||||
permission_classes = [
|
||||
IsAuthenticated,
|
||||
]
|
||||
|
||||
# Transfer the owner of the instance
|
||||
def post(self, request):
|
||||
instance = Instance.objects.first()
|
||||
|
||||
if str(instance.user_id) != str(request.user.id):
|
||||
# Check the accessing user
|
||||
if instance is not None and str(request.user.id) != str(instance.owner_id):
|
||||
return Response({"error": "Forbidden"}, status=status.HTTP_403_FORBIDDEN)
|
||||
|
||||
user_id = request.data.get("user_id", False)
|
||||
|
||||
if not user_id:
|
||||
# Get the email of the new user
|
||||
email = request.data.get("email", False)
|
||||
if not email:
|
||||
return Response(
|
||||
{"error": "User is required"}, status=status.HTTP_400_BAD_REQUEST
|
||||
)
|
||||
|
||||
user = User.objects.get(pk=user_id)
|
||||
# Get users
|
||||
user = User.objects.get(email=email)
|
||||
user.is_superuser = True
|
||||
user.save(update_fields=["is_superuser"])
|
||||
|
||||
# Save the instance user
|
||||
instance.owner = user
|
||||
instance.email = user.email
|
||||
instance.save(update_fields=["owner", "email"])
|
||||
|
||||
instance.user = user
|
||||
instance.save()
|
||||
return Response(
|
||||
{"message": "Owner successfully updated"}, status=status.HTTP_200_OK
|
||||
)
|
||||
|
@ -1,4 +1,4 @@
|
||||
# Generated by Django 4.2.5 on 2023-11-08 06:49
|
||||
# Generated by Django 4.2.5 on 2023-11-13 14:31
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
@ -15,23 +15,44 @@ class Migration(migrations.Migration):
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='InstanceConfiguration',
|
||||
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)),
|
||||
('key', models.CharField(max_length=100, unique=True)),
|
||||
('value', models.TextField(blank=True, default=None, null=True)),
|
||||
('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': 'Instance Configuration',
|
||||
'verbose_name_plural': 'Instance Configurations',
|
||||
'db_table': 'instance_configurations',
|
||||
'ordering': ('-created_at',),
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
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_name', models.CharField(max_length=255)),
|
||||
('whitelist_emails', models.TextField(blank=True, null=True)),
|
||||
('instance_id', models.CharField(max_length=25, unique=True)),
|
||||
('license_key', models.CharField(max_length=256)),
|
||||
('license_key', models.CharField(blank=True, max_length=256, null=True)),
|
||||
('api_key', models.CharField(max_length=16)),
|
||||
('version', models.CharField(max_length=10)),
|
||||
('email', models.CharField(max_length=256)),
|
||||
('last_checked_at', models.DateTimeField()),
|
||||
('namespace', models.CharField(blank=True, max_length=50, null=True)),
|
||||
('is_telemetry_enabled', models.BooleanField(default=True)),
|
||||
('is_support_required', models.BooleanField(default=True)),
|
||||
('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')),
|
||||
('owner', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='instance_owner', to=settings.AUTH_USER_MODEL)),
|
||||
('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': 'Instance',
|
||||
|
@ -1,34 +0,0 @@
|
||||
# Generated by Django 4.2.5 on 2023-11-08 11:15
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
('license', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='InstanceConfiguration',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Created At')),
|
||||
('updated_at', models.DateTimeField(auto_now=True, verbose_name='Last Modified At')),
|
||||
('key', models.CharField(max_length=100, unique=True)),
|
||||
('value', models.TextField()),
|
||||
('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': 'Instance Configuration',
|
||||
'verbose_name_plural': 'Instance Configurations',
|
||||
'db_table': 'instance_configurations',
|
||||
'ordering': ('-created_at',),
|
||||
},
|
||||
),
|
||||
]
|
@ -1,18 +0,0 @@
|
||||
# Generated by Django 4.2.5 on 2023-11-08 13:30
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('license', '0002_instanceconfiguration'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='instanceconfiguration',
|
||||
name='value',
|
||||
field=models.TextField(blank=True, null=True),
|
||||
),
|
||||
]
|
@ -1,18 +0,0 @@
|
||||
# Generated by Django 4.2.5 on 2023-11-08 14:12
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('license', '0003_alter_instanceconfiguration_value'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='instanceconfiguration',
|
||||
name='value',
|
||||
field=models.TextField(blank=True, default=None, null=True),
|
||||
),
|
||||
]
|
@ -7,18 +7,25 @@ from plane.db.models import BaseModel
|
||||
from plane.db.mixins import AuditModel
|
||||
|
||||
class Instance(BaseModel):
|
||||
# General informations
|
||||
instance_name = models.CharField(max_length=255)
|
||||
whitelist_emails = models.TextField(blank=True, null=True)
|
||||
instance_id = models.CharField(max_length=25, unique=True)
|
||||
license_key = models.CharField(max_length=256)
|
||||
license_key = models.CharField(max_length=256, null=True, blank=True)
|
||||
api_key = models.CharField(max_length=16)
|
||||
version = models.CharField(max_length=10)
|
||||
# User information
|
||||
email = models.CharField(max_length=256)
|
||||
user = models.ForeignKey(
|
||||
owner = models.ForeignKey(
|
||||
settings.AUTH_USER_MODEL,
|
||||
on_delete=models.SET_NULL,
|
||||
null=True,
|
||||
related_name="instance_owner",
|
||||
)
|
||||
# Instnace specifics
|
||||
last_checked_at = models.DateTimeField()
|
||||
namespace = models.CharField(max_length=50, blank=True, null=True)
|
||||
# telemetry and support
|
||||
is_telemetry_enabled = models.BooleanField(default=True)
|
||||
is_support_required = models.BooleanField(default=True)
|
||||
|
||||
@ -30,7 +37,8 @@ class Instance(BaseModel):
|
||||
|
||||
|
||||
|
||||
class InstanceConfiguration(AuditModel):
|
||||
class InstanceConfiguration(BaseModel):
|
||||
# The instance configuration variables
|
||||
key = models.CharField(max_length=100, unique=True)
|
||||
value = models.TextField(null=True, blank=True, default=None)
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user