rsnarsna commited on
Commit
eea4090
·
1 Parent(s): 0f3bc3b

Decouple organization creation from registration

Browse files
apps/accounts/forms.py CHANGED
@@ -45,46 +45,12 @@ class RegistrationForm(forms.Form):
45
  }
46
  ),
47
  )
48
- org_name = forms.CharField(
49
- max_length=255,
50
- widget=forms.TextInput(
51
- attrs={
52
- "class": "form-input",
53
- "placeholder": "Acme Corp",
54
- "id": "register-org-name",
55
- }
56
- ),
57
- label="Organization Name",
58
- )
59
- business_type = forms.ChoiceField(
60
- choices=[], # Populated dynamically in __init__
61
- widget=forms.Select(
62
- attrs={
63
- "class": "form-input",
64
- "id": "register-business-type",
65
- }
66
- ),
67
- label="Business Type",
68
- )
69
  terms_agreement = forms.BooleanField(
70
  required=True,
71
  error_messages={"required": "You must agree to the Terms of Service to register."},
72
  widget=forms.CheckboxInput(attrs={"class": "h-4 w-4 rounded border-gray-300 text-indigo-600 focus:ring-indigo-600", "id": "register-terms"}),
73
  )
74
 
75
- def __init__(self, *args, **kwargs):
76
- super().__init__(*args, **kwargs)
77
- # Fetch dynamic business types from SystemSetting
78
- try:
79
- allowed_types_setting = SystemSetting.objects.get(key="ALLOWED_BUSINESS_TYPES")
80
- types_list = allowed_types_setting.value
81
- if not isinstance(types_list, list):
82
- types_list = settings.DEFAULT_BUSINESS_TYPES
83
- except SystemSetting.DoesNotExist:
84
- types_list = settings.DEFAULT_BUSINESS_TYPES
85
-
86
- self.fields["business_type"].choices = [(bt, bt) for bt in types_list]
87
-
88
  def clean_email(self) -> str:
89
  email = self.cleaned_data["email"].lower()
90
  if User.objects.filter(email__iexact=email).exists():
 
45
  }
46
  ),
47
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
48
  terms_agreement = forms.BooleanField(
49
  required=True,
50
  error_messages={"required": "You must agree to the Terms of Service to register."},
51
  widget=forms.CheckboxInput(attrs={"class": "h-4 w-4 rounded border-gray-300 text-indigo-600 focus:ring-indigo-600", "id": "register-terms"}),
52
  )
53
 
 
 
 
 
 
 
 
 
 
 
 
 
 
54
  def clean_email(self) -> str:
55
  email = self.cleaned_data["email"].lower()
56
  if User.objects.filter(email__iexact=email).exists():
apps/accounts/services/auth_service.py CHANGED
@@ -51,14 +51,12 @@ class PasswordError(Exception):
51
  def register_user(
52
  email: str,
53
  password: str,
54
- org_name: str,
55
- business_type: str,
56
  terms_accepted: bool = False
57
  ) -> User:
58
  """
59
- Create a new user account with all associated records, and create their first Organization.
60
 
61
- Creates: User + Credentials + Profile + EmailVerification + Organization + OrgMembership.
62
  Returns the new User instance.
63
  """
64
  # Check against breached passwords
@@ -80,20 +78,12 @@ def register_user(
80
 
81
  EmailVerification.objects.create(user=user)
82
 
83
- # Create the initial Organization for this user
84
- from apps.organizations.services.org_service import create_org
85
- create_org(
86
- name=org_name,
87
- owner=user,
88
- vertical_type=business_type
89
- )
90
-
91
  # Log intelligence event
92
  from apps.intelligence.services import log_event
93
  log_event(
94
  event_type="user_registered",
95
  actor=user,
96
- metadata={"email_domain": email.split("@")[-1], "org_name": org_name, "business_type": business_type}
97
  )
98
 
99
  # Record Terms Consent
 
51
  def register_user(
52
  email: str,
53
  password: str,
 
 
54
  terms_accepted: bool = False
55
  ) -> User:
56
  """
57
+ Create a new user account with all associated records.
58
 
59
+ Creates: User + Credentials + Profile + EmailVerification.
60
  Returns the new User instance.
61
  """
62
  # Check against breached passwords
 
78
 
79
  EmailVerification.objects.create(user=user)
80
 
 
 
 
 
 
 
 
 
81
  # Log intelligence event
82
  from apps.intelligence.services import log_event
83
  log_event(
84
  event_type="user_registered",
85
  actor=user,
86
+ metadata={"email_domain": email.split("@")[-1]}
87
  )
88
 
89
  # Record Terms Consent
apps/accounts/views.py CHANGED
@@ -101,8 +101,6 @@ def register_view(request):
101
  user = register_user(
102
  email=form.cleaned_data["email"],
103
  password=form.cleaned_data["password"],
104
- org_name=form.cleaned_data["org_name"],
105
- business_type=form.cleaned_data["business_type"],
106
  terms_accepted=form.cleaned_data.get("terms_agreement", False),
107
  )
108
  # Conditional Verification or Auto-Login
 
101
  user = register_user(
102
  email=form.cleaned_data["email"],
103
  password=form.cleaned_data["password"],
 
 
104
  terms_accepted=form.cleaned_data.get("terms_agreement", False),
105
  )
106
  # Conditional Verification or Auto-Login
config/settings/__init__.py CHANGED
@@ -43,7 +43,7 @@ USE_X_FORWARDED_HOST = True
43
  SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
44
 
45
  # Subdomain Tenancy Settings
46
- BASE_DOMAIN = env("BASE_DOMAIN", default="localhost:9000")
47
 
48
  # Cookie Domain — share sessions & CSRF across all *.localhost subdomains
49
  # Use None in production unless you explicitly need a wild card subdomain cookie.
@@ -52,9 +52,9 @@ CSRF_COOKIE_DOMAIN = env("CSRF_COOKIE_DOMAIN", default=None)
52
 
53
  # Trust all subdomains for CSRF
54
  CSRF_TRUSTED_ORIGINS = env.list("CSRF_TRUSTED_ORIGINS", default=[
55
- "http://*.localhost:9000",
56
- "http://localhost:9000",
57
- "http://127.0.0.1:9000",
58
  "https://*.hf.space",
59
  "https://*.ngrok-free.app",
60
  "https://*.ngrok.io",
@@ -62,7 +62,7 @@ CSRF_TRUSTED_ORIGINS = env.list("CSRF_TRUSTED_ORIGINS", default=[
62
  ])
63
 
64
  # Site URL (Used for email verification links)
65
- SITE_URL = "http://localhost:9000"
66
 
67
  INTERNAL_IPS = [] # Added for Django Debug Toolbar, if needed.
68
 
 
43
  SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
44
 
45
  # Subdomain Tenancy Settings
46
+ BASE_DOMAIN = env("BASE_DOMAIN", default="localhost:8000")
47
 
48
  # Cookie Domain — share sessions & CSRF across all *.localhost subdomains
49
  # Use None in production unless you explicitly need a wild card subdomain cookie.
 
52
 
53
  # Trust all subdomains for CSRF
54
  CSRF_TRUSTED_ORIGINS = env.list("CSRF_TRUSTED_ORIGINS", default=[
55
+ "http://*.localhost:8000",
56
+ "http://localhost:8000",
57
+ "http://127.0.0.1:8000",
58
  "https://*.hf.space",
59
  "https://*.ngrok-free.app",
60
  "https://*.ngrok.io",
 
62
  ])
63
 
64
  # Site URL (Used for email verification links)
65
+ SITE_URL = "http://localhost:8000"
66
 
67
  INTERNAL_IPS = [] # Added for Django Debug Toolbar, if needed.
68
 
docker-compose.yml CHANGED
@@ -19,7 +19,7 @@ services:
19
  volumes:
20
  - .:/app
21
  ports:
22
- - "9000:7860"
23
  environment:
24
  - DATABASE_URL=postgres://postgres:postgres@db:5432/saas_db
25
  - DJANGO_SETTINGS_MODULE=config.settings
 
19
  volumes:
20
  - .:/app
21
  ports:
22
+ - "8000:7860"
23
  environment:
24
  - DATABASE_URL=postgres://postgres:postgres@db:5432/saas_db
25
  - DJANGO_SETTINGS_MODULE=config.settings
start_dev.ps1 CHANGED
@@ -45,5 +45,5 @@ Write-Host "Containers are up and running!" -ForegroundColor Green
45
 
46
  # 3. Start ngrok
47
  Write-Host "`n[3/3] Generating public ngrok URL..." -ForegroundColor Yellow
48
- Write-Host "Starting ngrok on port 9000..." -ForegroundColor Green
49
- ngrok http 9000
 
45
 
46
  # 3. Start ngrok
47
  Write-Host "`n[3/3] Generating public ngrok URL..." -ForegroundColor Yellow
48
+ Write-Host "Starting ngrok on port 8000..." -ForegroundColor Green
49
+ ngrok http 8000
templates/pages/accounts/register.html CHANGED
@@ -45,34 +45,7 @@
45
  {% endif %}
46
  </div>
47
 
48
- <div class="relative mt-6">
49
- <div class="absolute inset-0 flex items-center"><div class="w-full border-t border-[--border]"></div></div>
50
- <div class="relative flex justify-center text-sm/6"><span class="bg-[--card] px-6 text-[--muted-foreground] font-semibold uppercase tracking-wider text-xs">Organization Details</span></div>
51
- </div>
52
 
53
- <div>
54
- <label for="id_org_name" class="block text-sm/6 font-medium text-[--foreground]">Organization Name</label>
55
- <div class="mt-2">
56
- <input id="id_org_name" type="text" name="org_name" required placeholder="Acme Corp" class="block w-full rounded-md bg-[--input] px-3 py-1.5 text-base text-[--foreground] outline-1 -outline-offset-1 outline-[--border] placeholder:text-[--muted-foreground] focus:outline-2 focus:-outline-offset-2 focus:outline-[--primary] sm:text-sm/6" />
57
- </div>
58
- {% if form.org_name.errors %}
59
- <p class="mt-1 text-sm text-[--destructive]">{{ form.org_name.errors[0] }}</p>
60
- {% endif %}
61
- </div>
62
-
63
- <div>
64
- <label for="id_business_type" class="block text-sm/6 font-medium text-[--foreground]">Business Type</label>
65
- <div class="mt-2">
66
- <select id="id_business_type" name="business_type" required class="block w-full rounded-md bg-[--input] px-3 py-2 text-base text-[--foreground] outline-1 -outline-offset-1 outline-[--border] focus:outline-2 focus:-outline-offset-2 focus:outline-[--primary] sm:text-sm/6">
67
- {% for choice_val, choice_label in form.fields.business_type.choices %}
68
- <option value="{{ choice_val }}">{{ choice_label }}</option>
69
- {% endfor %}
70
- </select>
71
- </div>
72
- {% if form.business_type.errors %}
73
- <p class="mt-1 text-sm text-[--destructive]">{{ form.business_type.errors[0] }}</p>
74
- {% endif %}
75
- </div>
76
 
77
  <div class="flex items-start mt-6">
78
  <div class="flex h-6 items-center">
 
45
  {% endif %}
46
  </div>
47
 
 
 
 
 
48
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
49
 
50
  <div class="flex items-start mt-6">
51
  <div class="flex h-6 items-center">