From d2fb1fdc5e983e90e5ecd62080b0bdaec1da1fb5 Mon Sep 17 00:00:00 2001 From: ngovinh2k2 Date: Wed, 21 Jan 2026 14:14:29 +0700 Subject: [PATCH] refactor: optimize init organization file and handle broker --- .github/pull_request_template.md | 33 +++++++------- apps/authentication/serializers.py | 10 +++-- .../management/commands/init_organization.py | 45 +++++++++---------- bootstrap_service/settings.py | 12 +++++ bootstrap_service/urls.py | 8 ++-- docker-entrypoint.sh | 14 +++--- requirements.txt | 3 +- setup.cfg | 4 +- 8 files changed, 74 insertions(+), 55 deletions(-) diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 2ae0a23..3851a9b 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -1,26 +1,27 @@ ## What? -Explain the changes you've made -## Why? -The “why” tells us what business or engineering goal this change achieves -## How? - -## Testing? -[ ] Functional Testing - -[ ] Security -[ ] Performance + -[ ] Error Handling +## Why? -[ ] Code Quality + -[ ] Documentation +## How? -[ ] Database + -[ ] Deployment +## Testing? -[ ] Final Review +- [ ] Functional Testing +- [ ] Security +- [ ] Performance +- [ ] Error Handling +- [ ] Code Quality +- [ ] Documentation +- [ ] Database +- [ ] Deployment +- [ ] Final Review ## Anything Else? + + \ No newline at end of file diff --git a/apps/authentication/serializers.py b/apps/authentication/serializers.py index 8fba35b..ca2c097 100644 --- a/apps/authentication/serializers.py +++ b/apps/authentication/serializers.py @@ -30,12 +30,16 @@ def get_tokens(self): self.user, organization_slug=default_organization_slug ) - return refresh_token, access_token + return refresh_token, access_token, default_organization_slug def get_response_data(self): - refresh_token, access_token = self.get_tokens() + refresh_token, access_token, default_organization = self.get_tokens() - return {"refresh": str(refresh_token), "access": str(access_token)} + return { + "refresh": str(refresh_token), + "access": str(access_token), + "default_organization": default_organization, + } class AuthTokenPairSerializer(TokenPairSerializer): diff --git a/bootstrap_service/management/commands/init_organization.py b/bootstrap_service/management/commands/init_organization.py index dc4537d..337bb19 100644 --- a/bootstrap_service/management/commands/init_organization.py +++ b/bootstrap_service/management/commands/init_organization.py @@ -16,13 +16,12 @@ import uuid from datetime import datetime +from common.celery.task_senders import send_task from common.rabitmq.rabbitmq_provisioner import RabbitMQProvisioner -from django.conf import settings from django.contrib.auth.hashers import make_password from django.core.management.base import BaseCommand +from django.forms.models import model_to_dict from django.utils import timezone -from django.utils.module_loading import import_string -from kombu import Exchange from apps.authentication.models import RootUser from apps.organization.models import Organization @@ -102,9 +101,10 @@ def _publish_org_event(self, org_id, org_slug, org_name, result): }, ) - def _create_organization_with_roles(self, org_name, org_slug, result): + def _create_organization_with_roles(self, org_id, org_name, org_slug, result): """Create organization with default policies and roles.""" organization = Organization.objects.create( + id=org_id, name=org_name, slug_name=org_slug, logo="", @@ -135,23 +135,23 @@ def _create_organization_with_roles(self, org_name, org_slug, result): self.stdout.write(self.style.SUCCESS("Created default roles")) return organization, owner_role - def _send_celery_task(self, org_id, org_name, org_slug, user, encrypted_password): + def _send_celery_task(self, org_id, org_name, org_slug, user): """Send initialization task to Celery.""" - celery_app = import_string(settings.CELERY_APP) - celery_app.send_task( - name="spacedf.tasks.new_organization", - exchange=Exchange("new_organization", type="fanout"), - routing_key="new_organization", - kwargs={ + send_task( + name="new_organization", + message={ "id": org_id, "name": org_name, "slug_name": org_slug, "is_active": True, - "owner": { - "id": str(user.id), - "email": user.email, - "password": encrypted_password, - }, + "owner": model_to_dict( + user, + fields=[ + "id", + "email", + "password", + ], + ), "created_at": datetime.now().isoformat(), "updated_at": datetime.now().isoformat(), }, @@ -159,12 +159,9 @@ def _send_celery_task(self, org_id, org_name, org_slug, user, encrypted_password def _send_delete_celery_task(self, org_slug): """Send organization deletion task to Celery.""" - celery_app = import_string(settings.CELERY_APP) - celery_app.send_task( - name="spacedf.tasks.delete_organization", - exchange=Exchange("delete_organization", type="fanout"), - routing_key="delete_organization", - kwargs={ + send_task( + name="delete_organization", + message={ "slug_name": org_slug, }, ) @@ -227,7 +224,7 @@ def handle(self, *args, **kwargs): ) self.stdout.write(self.style.SUCCESS(f"Created owner user: {owner_email}")) _, owner_role = self._create_organization_with_roles( - org_name, org_slug, result + org_id, org_name, org_slug, result ) OrganizationRoleUser(root_user=user, organization_role=owner_role).save() self.stdout.write( @@ -236,7 +233,7 @@ def handle(self, *args, **kwargs): # Publish organization event self._publish_org_event(org_id, org_slug, org_name, result) - self._send_celery_task(org_id, org_name, org_slug, user, encrypted_password) + self._send_celery_task(org_id, org_name, org_slug, user) else: self.stdout.write( self.style.WARNING( diff --git a/bootstrap_service/settings.py b/bootstrap_service/settings.py index 3e3540a..3ee3fb3 100644 --- a/bootstrap_service/settings.py +++ b/bootstrap_service/settings.py @@ -162,6 +162,8 @@ ORG_DISCOVERY_ROUTING_KEY = "org.discovery.request" # Celery Configuration (for sending tasks only) +CELERY_ACKS_LATE = True +CELERYD_PREFETCH_MULTIPLIER = 1 CELERY_APP = "bootstrap_service.celery.app" CELERY_BROKER_URL = os.getenv("CELERY_BROKER_URL", "amqp://default:password@rabbitmq") CELERY_BROKER_CONNECTION_RETRY_ON_STARTUP = True @@ -206,3 +208,13 @@ DEFAULT_FROM_EMAIL = os.getenv("DEFAULT_FROM_EMAIL", "") EMAIL_HOST_USER = os.getenv("EMAIL_HOST_USER", "") EMAIL_HOST_PASSWORD = os.getenv("EMAIL_HOST_PASSWORD", "") + +CACHES = { + "default": { + "BACKEND": "django_redis.cache.RedisCache", + "LOCATION": os.getenv("REDIS_HOST", "redis://127.0.0.1:6379/1"), + "OPTIONS": { + "CLIENT_CLASS": "django_redis.client.DefaultClient", + }, + } +} diff --git a/bootstrap_service/urls.py b/bootstrap_service/urls.py index 36fbf3b..1eca210 100644 --- a/bootstrap_service/urls.py +++ b/bootstrap_service/urls.py @@ -24,11 +24,11 @@ schema_view = get_schema_view( openapi.Info( - title="SPACEDF CONSOLE API", + title="SPACEDF BOOTSTRAP API", default_version="v1", - terms_of_service="https://www.google.com/policies/terms/", - contact=openapi.Contact(email="contact@snippets.local"), - license=openapi.License(name="BSD License"), + terms_of_service="https://spacedf.com/terms-of-service", + contact=openapi.Contact(email="hello@df.technology"), + license=openapi.License(name="Apache 2.0"), ), public=True, permission_classes=[permissions.AllowAny], diff --git a/docker-entrypoint.sh b/docker-entrypoint.sh index 91cbf99..38c27b5 100644 --- a/docker-entrypoint.sh +++ b/docker-entrypoint.sh @@ -23,6 +23,10 @@ echo "EMQX is ready" echo "Running database migrations..." python manage.py migrate +echo "Starting Celery worker..." +celery -A bootstrap_service worker -l info -c 1 & +sleep 5 + echo "Running organization initialization..." python manage.py init_organization \ --org-name="${ORG_NAME}" \ @@ -30,8 +34,8 @@ python manage.py init_organization \ --owner-email="${OWNER_EMAIL}" \ --owner-password="${OWNER_PASSWORD}" -echo "Organization initialization complete" - -# Start Gunicorn and Celery -gunicorn --worker-class gevent --bind 0.0.0.0:80 --access-logfile - bootstrap_service.wsgi \ -& celery -A bootstrap_service worker -l info -c 1 +echo "Starting Gunicorn..." +exec gunicorn bootstrap_service.wsgi:application \ + --worker-class gevent \ + --bind 0.0.0.0:80 \ + --access-logfile - diff --git a/requirements.txt b/requirements.txt index 18b065c..f622631 100644 --- a/requirements.txt +++ b/requirements.txt @@ -15,4 +15,5 @@ django-environ==0.11.2 pika==1.3.2 django-redis==5.4.0 boto3==1.37.13 -psycopg2-binary==2.9.9 \ No newline at end of file +psycopg2-binary==2.9.9 +django_tenants==3.6.1 \ No newline at end of file diff --git a/setup.cfg b/setup.cfg index 01e8981..3916337 100644 --- a/setup.cfg +++ b/setup.cfg @@ -4,7 +4,7 @@ exclude = .tox,.git,*/migrations/*,*/static/CACHE/*,docs,node_modules,venv [isort] line_length = 88 -known_first_party = console-service +known_first_party = bootstrap-service multi_line_output = 3 default_section = THIRDPARTY skip = venv/ @@ -14,7 +14,7 @@ force_grid_wrap = 0 use_parentheses = true [coverage:run] -include = console-service/* +include = bootstrap-service/* omit = *migrations*, *tests* plugins = django_coverage_plugin