diff --git a/sahara/db.sqlite3 b/sahara/db.sqlite3 index a9ec1ea..39db227 100644 Binary files a/sahara/db.sqlite3 and b/sahara/db.sqlite3 differ diff --git a/sahara/main/__pycache__/__init__.cpython-312.pyc b/sahara/main/__pycache__/__init__.cpython-312.pyc index 08c17ff..03635d8 100644 Binary files a/sahara/main/__pycache__/__init__.cpython-312.pyc and b/sahara/main/__pycache__/__init__.cpython-312.pyc differ diff --git a/sahara/main/__pycache__/admin.cpython-312.pyc b/sahara/main/__pycache__/admin.cpython-312.pyc index 736ac7f..bf855af 100644 Binary files a/sahara/main/__pycache__/admin.cpython-312.pyc and b/sahara/main/__pycache__/admin.cpython-312.pyc differ diff --git a/sahara/main/__pycache__/apps.cpython-312.pyc b/sahara/main/__pycache__/apps.cpython-312.pyc index 8d62f81..ee52d2f 100644 Binary files a/sahara/main/__pycache__/apps.cpython-312.pyc and b/sahara/main/__pycache__/apps.cpython-312.pyc differ diff --git a/sahara/main/__pycache__/forms.cpython-312.pyc b/sahara/main/__pycache__/forms.cpython-312.pyc index c5b5796..4fb3138 100644 Binary files a/sahara/main/__pycache__/forms.cpython-312.pyc and b/sahara/main/__pycache__/forms.cpython-312.pyc differ diff --git a/sahara/main/__pycache__/managers.cpython-312.pyc b/sahara/main/__pycache__/managers.cpython-312.pyc deleted file mode 100644 index 4369e48..0000000 Binary files a/sahara/main/__pycache__/managers.cpython-312.pyc and /dev/null differ diff --git a/sahara/main/__pycache__/models.cpython-312.pyc b/sahara/main/__pycache__/models.cpython-312.pyc index 49e3c15..250731b 100644 Binary files a/sahara/main/__pycache__/models.cpython-312.pyc and b/sahara/main/__pycache__/models.cpython-312.pyc differ diff --git a/sahara/main/__pycache__/urls.cpython-312.pyc b/sahara/main/__pycache__/urls.cpython-312.pyc index 1bc0233..5a051a8 100644 Binary files a/sahara/main/__pycache__/urls.cpython-312.pyc and b/sahara/main/__pycache__/urls.cpython-312.pyc differ diff --git a/sahara/main/__pycache__/views.cpython-312.pyc b/sahara/main/__pycache__/views.cpython-312.pyc index 8adbc42..2f14f32 100644 Binary files a/sahara/main/__pycache__/views.cpython-312.pyc and b/sahara/main/__pycache__/views.cpython-312.pyc differ diff --git a/sahara/main/admin.py b/sahara/main/admin.py index bf461d9..bab3cf1 100644 --- a/sahara/main/admin.py +++ b/sahara/main/admin.py @@ -1,8 +1,37 @@ from django.contrib import admin +from django.contrib.auth.admin import UserAdmin +from .models import User # Import your custom User model -from .models import BaseUser +class CustomUserAdmin(UserAdmin): + model = User -# Register your models here. + # Fields to display in the list view of the admin + list_display = ['email', 'first_name', 'last_name', 'is_staff', 'is_superuser', 'is_active'] -admin.site.register(BaseUser) + # Fields to search in the admin + search_fields = ['email', 'first_name', 'last_name'] + # Default ordering of records in the admin + ordering = ['email'] + + # Fieldsets for the add and change forms in the admin + fieldsets = ( + (None, {'fields': ('email', 'password')}), + ('Personal Info', {'fields': ('first_name', 'last_name')}), + ('Permissions', {'fields': ('is_active', 'is_staff', 'is_superuser')}), + ('Important Dates', {'fields': ('last_login',)}), # Remove 'date_joined' if it doesn't exist + ) + + # Fieldsets for the add form in the admin + add_fieldsets = ( + (None, { + 'classes': ('wide',), + 'fields': ('email', 'first_name', 'last_name', 'password1', 'password2', 'is_staff', 'is_superuser', 'is_active'), + }), + ) + + # Filters for the list view in the admin + list_filter = ('is_staff', 'is_superuser', 'is_active') + +# Register your custom User model with the custom UserAdmin class +admin.site.register(User, CustomUserAdmin) \ No newline at end of file diff --git a/sahara/main/forms.py b/sahara/main/forms.py index b8a7f9a..5f6afaf 100644 --- a/sahara/main/forms.py +++ b/sahara/main/forms.py @@ -1,45 +1,52 @@ from django import forms -from django.contrib.auth.forms import UserCreationForm -from .models import BaseUser +from django.contrib.auth.forms import AuthenticationForm +from .models import User -class BaseUserForm(UserCreationForm): - class Meta: - model = BaseUser - fields = [ - 'first_name', - 'last_name', - 'bio', - 'address', - 'contact_number', - 'image', - 'citizenship', - 'certificate', - 'price', - 'role', - 'email', - ] - password1 = forms.CharField( - widget=forms.PasswordInput(attrs={'class': 'form-control', 'placeholder': 'Password'}), - label='Password', - min_length=8, +class UserRegistrationForm(forms.ModelForm): + password = forms.CharField( + widget=forms.PasswordInput(attrs={ + 'class': 'form-control', + 'placeholder': 'Enter your password', + }) ) - password2 = forms.CharField( - widget=forms.PasswordInput(attrs={'class': 'form-control', 'placeholder': 'Confirm Password'}), - label='Confirm Password', - min_length=8, + confirm_password = forms.CharField( + widget=forms.PasswordInput(attrs={ + 'class': 'form-control', + 'placeholder': 'Confirm your password', + }) ) - def clean_password2(self): - password1 = self.cleaned_data.get('password1') - password2 = self.cleaned_data.get('password2') + class Meta: + model = User + fields = ['first_name', 'last_name', 'email', 'password'] + widgets = { + 'first_name': forms.TextInput(attrs={ + 'class': 'form-control', + 'placeholder': 'Enter your first name', + }), + 'last_name': forms.TextInput(attrs={ + 'class': 'form-control', + 'placeholder': 'Enter your last name', + }), + 'email': forms.EmailInput(attrs={ + 'class': 'form-control', + 'placeholder': 'Enter your email address', + }), + } + + def clean(self): + cleaned_data = super().clean() + password = cleaned_data.get('password') + confirm_password = cleaned_data.get('confirm_password') - if password1 and password2 and password1 != password2: - raise forms.ValidationError("Passwords don't match.") - return password2 + if password and confirm_password and password != confirm_password: + raise forms.ValidationError("Passwords do not match.") - def save(self, commit=True): - user = super().save(commit=False) - user.set_password(self.cleaned_data['password1']) - if commit: - user.save() - return user +class UserLoginForm(AuthenticationForm): + def __init__(self, *args, **kwargs): + self.request = kwargs.pop('request', None) + super(UserLoginForm, self).__init__(*args, **kwargs) + # Change the username field label and placeholder to 'Email' + self.fields['username'].label = 'Email' + self.fields['username'].widget.attrs.update({'placeholder': 'Enter your email'}) + self.fields['password'].widget.attrs.update({'placeholder': 'Enter your password'}) diff --git a/sahara/main/migrations/0001_initial.py b/sahara/main/migrations/0001_initial.py index c2014a6..2039572 100644 --- a/sahara/main/migrations/0001_initial.py +++ b/sahara/main/migrations/0001_initial.py @@ -1,8 +1,5 @@ -# Generated by Django 5.1.4 on 2025-01-11 12:36 +# Generated by Django 5.1.4 on 2025-01-11 17:02 -import django.contrib.auth.models -import django.contrib.auth.validators -import django.utils.timezone from django.db import migrations, models @@ -16,41 +13,22 @@ class Migration(migrations.Migration): operations = [ migrations.CreateModel( - name='BaseUser', + name='User', fields=[ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('password', models.CharField(max_length=128, verbose_name='password')), ('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')), - ('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')), - ('username', models.CharField(error_messages={'unique': 'A user with that username already exists.'}, help_text='Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.', max_length=150, unique=True, validators=[django.contrib.auth.validators.UnicodeUsernameValidator()], verbose_name='username')), - ('first_name', models.CharField(blank=True, max_length=150, verbose_name='first name')), - ('last_name', models.CharField(blank=True, max_length=150, verbose_name='last name')), - ('email', models.EmailField(blank=True, max_length=254, verbose_name='email address')), - ('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')), - ('is_active', models.BooleanField(default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')), - ('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')), - ('image', models.ImageField(blank=True, null=True, upload_to='user_profiles/')), - ('bio', models.TextField(blank=True, null=True)), - ('address', models.TextField(blank=True, null=True)), - ('contact_number', models.CharField(blank=True, max_length=15, null=True)), - ('citizenship', models.CharField(blank=True, max_length=100, null=True)), - ('certificate', models.FileField(blank=True, null=True, upload_to='certificates/')), - ('is_verified', models.BooleanField(default=False)), - ('price', models.DecimalField(blank=True, decimal_places=2, max_digits=10, null=True)), - ('ratings', models.DecimalField(decimal_places=2, default=0.0, max_digits=3)), - ('role', models.CharField(choices=[('CLIENT', 'CLIENT'), ('ADMIN', 'ADMIN'), ('SERVICE_PROVIDER', 'SERVICE_PROVIDER')], default='CLIENT', max_length=50)), - ('member_since', models.DateTimeField(auto_now_add=True)), - ('profile_updated', models.DateTimeField(auto_now=True)), - ('token', models.CharField(blank=True, max_length=256, null=True)), + ('email', models.EmailField(max_length=254, unique=True)), + ('first_name', models.CharField(max_length=30)), + ('last_name', models.CharField(max_length=30)), + ('is_active', models.BooleanField(default=True)), + ('is_staff', models.BooleanField(default=False)), + ('is_superuser', models.BooleanField(default=False)), ('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.group', verbose_name='groups')), ('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.permission', verbose_name='user permissions')), ], options={ - 'verbose_name': 'Base User', - 'verbose_name_plural': 'Base Users', + 'abstract': False, }, - managers=[ - ('objects', django.contrib.auth.models.UserManager()), - ], ), ] diff --git a/sahara/main/migrations/0002_user_bio_user_member_since_user_price_user_profile_and_more.py b/sahara/main/migrations/0002_user_bio_user_member_since_user_price_user_profile_and_more.py new file mode 100644 index 0000000..95499b1 --- /dev/null +++ b/sahara/main/migrations/0002_user_bio_user_member_since_user_price_user_profile_and_more.py @@ -0,0 +1,43 @@ +# Generated by Django 5.1.4 on 2025-01-11 19:36 + +import datetime +import django.utils.timezone +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('main', '0001_initial'), + ] + + operations = [ + migrations.AddField( + model_name='user', + name='bio', + field=models.CharField(default=datetime.datetime(2025, 1, 11, 19, 36, 0, 579795, tzinfo=datetime.timezone.utc), max_length=200), + preserve_default=False, + ), + migrations.AddField( + model_name='user', + name='member_since', + field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now), + preserve_default=False, + ), + migrations.AddField( + model_name='user', + name='price', + field=models.PositiveBigIntegerField(default=10), + preserve_default=False, + ), + migrations.AddField( + model_name='user', + name='profile', + field=models.ImageField(blank=True, null=True, upload_to=''), + ), + migrations.AddField( + model_name='user', + name='updated_on', + field=models.DateField(auto_now=True), + ), + ] diff --git a/sahara/main/migrations/__pycache__/0001_initial.cpython-312.pyc b/sahara/main/migrations/__pycache__/0001_initial.cpython-312.pyc index 0a08844..0cb52ae 100644 Binary files a/sahara/main/migrations/__pycache__/0001_initial.cpython-312.pyc and b/sahara/main/migrations/__pycache__/0001_initial.cpython-312.pyc differ diff --git a/sahara/main/migrations/__pycache__/0002_alter_baseuser_options_baseuser_date_joined_and_more.cpython-312.pyc b/sahara/main/migrations/__pycache__/0002_alter_baseuser_options_baseuser_date_joined_and_more.cpython-312.pyc deleted file mode 100644 index bf1341d..0000000 Binary files a/sahara/main/migrations/__pycache__/0002_alter_baseuser_options_baseuser_date_joined_and_more.cpython-312.pyc and /dev/null differ diff --git a/sahara/main/migrations/__pycache__/0002_alter_baseuser_username.cpython-312.pyc b/sahara/main/migrations/__pycache__/0002_alter_baseuser_username.cpython-312.pyc deleted file mode 100644 index b1485a6..0000000 Binary files a/sahara/main/migrations/__pycache__/0002_alter_baseuser_username.cpython-312.pyc and /dev/null differ diff --git a/sahara/main/migrations/__pycache__/0002_user_bio_user_member_since_user_price_user_profile_and_more.cpython-312.pyc b/sahara/main/migrations/__pycache__/0002_user_bio_user_member_since_user_price_user_profile_and_more.cpython-312.pyc new file mode 100644 index 0000000..f71382a Binary files /dev/null and b/sahara/main/migrations/__pycache__/0002_user_bio_user_member_since_user_price_user_profile_and_more.cpython-312.pyc differ diff --git a/sahara/main/migrations/__pycache__/0003_alter_baseuser_price.cpython-312.pyc b/sahara/main/migrations/__pycache__/0003_alter_baseuser_price.cpython-312.pyc deleted file mode 100644 index 2ad9068..0000000 Binary files a/sahara/main/migrations/__pycache__/0003_alter_baseuser_price.cpython-312.pyc and /dev/null differ diff --git a/sahara/main/migrations/__pycache__/0003_remove_baseuser_role.cpython-312.pyc b/sahara/main/migrations/__pycache__/0003_remove_baseuser_role.cpython-312.pyc deleted file mode 100644 index 945339c..0000000 Binary files a/sahara/main/migrations/__pycache__/0003_remove_baseuser_role.cpython-312.pyc and /dev/null differ diff --git a/sahara/main/migrations/__pycache__/0004_alter_baseuser_managers_baseuser_role.cpython-312.pyc b/sahara/main/migrations/__pycache__/0004_alter_baseuser_managers_baseuser_role.cpython-312.pyc deleted file mode 100644 index efc3cce..0000000 Binary files a/sahara/main/migrations/__pycache__/0004_alter_baseuser_managers_baseuser_role.cpython-312.pyc and /dev/null differ diff --git a/sahara/main/migrations/__pycache__/0005_alter_baseuser_managers.cpython-312.pyc b/sahara/main/migrations/__pycache__/0005_alter_baseuser_managers.cpython-312.pyc deleted file mode 100644 index 94be69c..0000000 Binary files a/sahara/main/migrations/__pycache__/0005_alter_baseuser_managers.cpython-312.pyc and /dev/null differ diff --git a/sahara/main/migrations/__pycache__/0006_remove_baseuser_username.cpython-312.pyc b/sahara/main/migrations/__pycache__/0006_remove_baseuser_username.cpython-312.pyc deleted file mode 100644 index b2f6519..0000000 Binary files a/sahara/main/migrations/__pycache__/0006_remove_baseuser_username.cpython-312.pyc and /dev/null differ diff --git a/sahara/main/migrations/__pycache__/__init__.cpython-312.pyc b/sahara/main/migrations/__pycache__/__init__.cpython-312.pyc index aac4277..732ba5a 100644 Binary files a/sahara/main/migrations/__pycache__/__init__.cpython-312.pyc and b/sahara/main/migrations/__pycache__/__init__.cpython-312.pyc differ diff --git a/sahara/main/models.py b/sahara/main/models.py index efa7069..fb77b44 100644 --- a/sahara/main/models.py +++ b/sahara/main/models.py @@ -1,41 +1,110 @@ -from django.contrib.auth.models import AbstractUser from django.db import models +from django.contrib.auth.models import AbstractBaseUser, BaseUserManager, PermissionsMixin +from django.core.mail import send_mail +from django.utils.encoding import force_bytes +from django.template.loader import render_to_string +from django.contrib.auth.tokens import default_token_generator +from django.conf import settings +from django.utils.http import urlsafe_base64_encode -class BaseUser(AbstractUser): - # Additional fields to extend the default user model - - # Profile-related fields - image = models.ImageField(upload_to='user_profiles/', blank=True, null=True) - bio = models.TextField(blank=True, null=True) - address = models.TextField(blank=True, null=True) - contact_number = models.CharField(max_length=15, blank=True, null=True) - citizenship = models.CharField(max_length=100, blank=True, null=True) - certificate = models.FileField(upload_to='certificates/', blank=True, null=True) - - # Verification and status - is_verified = models.BooleanField(default=False) - - # Financial or business-related fields - price = models.DecimalField(max_digits=10, decimal_places=2, blank=True, null=True) - ratings = models.DecimalField(max_digits=3, decimal_places=2, default=0.00) - - # Role and other identifiers - role = models.CharField( - max_length=50, - choices=[('CLIENT', 'CLIENT'), ('ADMIN', 'ADMIN'), ('SERVICE_PROVIDER', 'SERVICE_PROVIDER')], - default='CLIENT' - ) - - # Time-related fields + +class Service(models.Model): + name = models.CharField(max_length=20) + description = models.CharField(max_length=100, blank=True, null=True) + def __str__(self): + return self.name + +class UserManager(BaseUserManager): + def create_user(self, first_name, last_name, email, password=None, **extra_fields): + """ + Creates and saves a User with the given first name, last name, email, and password. + """ + if not email: + raise ValueError('Users must have an email address') + + email = self.normalize_email(email) + user = self.model( + first_name=first_name, + last_name=last_name, + email=email, + **extra_fields + ) + user.set_password(password) + user.save(using=self._db) + + # Send verification email + try: + self.send_verification_email(user) + except: + print("Couldnot send verification email.") + + return user + + def create_superuser(self, first_name, last_name, email, password=None, **extra_fields): + """ + Creates and saves a superuser with the given first name, last name, email, and password. + """ + extra_fields.setdefault('is_staff', True) + extra_fields.setdefault('is_superuser', True) + extra_fields.setdefault('is_active', True) + + if extra_fields.get('is_staff') is not True: + raise ValueError('Superuser must have is_staff=True.') + if extra_fields.get('is_superuser') is not True: + raise ValueError('Superuser must have is_superuser=True.') + + return self.create_user(first_name, last_name, email, password, **extra_fields) + + def send_verification_email(self, user): + # Generate the token and the uid for the verification URL + token = default_token_generator.make_token(user) + uid = urlsafe_base64_encode(force_bytes(user.pk)) + verification_url = f"{settings.FRONTEND_URL}/verify-email/{uid}/{token}/" + + subject = "Verify Your Email Address" + message = render_to_string('emails/verification_email.html', { + 'user': user, + 'verification_url': verification_url, + }) + + send_mail( + subject, + message, + settings.DEFAULT_FROM_EMAIL, + [user.email], + fail_silently=False, + ) + + +class User(AbstractBaseUser, PermissionsMixin): + email = models.EmailField(unique=True) + first_name = models.CharField(max_length=30) + last_name = models.CharField(max_length=30) + is_active = models.BooleanField(default=True) + is_staff = models.BooleanField(default=False) + is_superuser = models.BooleanField(default=False) + profile = models.ImageField(blank=True,null=True) + price = models.PositiveBigIntegerField(blank=True, null=True) + bio = models.CharField(max_length=200, blank=True, null=True) member_since = models.DateTimeField(auto_now_add=True) - profile_updated = models.DateTimeField(auto_now=True) - - # Token field for verification or authentication purposes - token = models.CharField(max_length=256, blank=True, null=True) - + updated_on = models.DateField(auto_now=True) + USERNAME_FIELD = 'email' + REQUIRED_FIELDS = ['first_name', 'last_name'] + + objects = UserManager() + def __str__(self): - return self.username - - class Meta: - verbose_name = 'Base User' - verbose_name_plural = 'Base Users' + return self.email + + def get_full_name(self): + return f"{self.first_name} {self.last_name}" + + def get_short_name(self): + return self.first_name + +class ServiceRequest(models.Model): + client = models.ForeignKey(User,on_delete=models.CASCADE,related_name="client") + service_provider = models.ForeignKey(User,on_delete=models.CASCADE,related_name="client") + agreed_price = models.DecimalField(max_digits=999,decimal_places=2 , default=0) + agreed_on = models.DateField(auto_now_add=True) + is_completed = models.BooleanField(default=False) diff --git a/sahara/main/templates/emails/verification_email.html b/sahara/main/templates/emails/verification_email.html new file mode 100644 index 0000000..6f82145 --- /dev/null +++ b/sahara/main/templates/emails/verification_email.html @@ -0,0 +1,12 @@ + + + + Verify Your Email Address + + +

Hello {{ user.get_full_name }},

+

Please click the link below to verify your email address:

+

{{ verification_url }}

+

If you did not create an account, please ignore this email.

+ + \ No newline at end of file diff --git a/sahara/main/templates/main/create_user.html b/sahara/main/templates/main/create_user.html deleted file mode 100644 index fe40825..0000000 --- a/sahara/main/templates/main/create_user.html +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - Document - - - -
- {% csrf_token %} - {{ form.as_p }} - -
- - diff --git a/sahara/main/templates/main/delete_user.html b/sahara/main/templates/main/delete_user.html deleted file mode 100644 index 296dbec..0000000 --- a/sahara/main/templates/main/delete_user.html +++ /dev/null @@ -1,5 +0,0 @@ -
- {% csrf_token %} -

Are you sure you want to delete {{ object }}?

- -
\ No newline at end of file diff --git a/sahara/main/templates/main/home.html b/sahara/main/templates/main/home.html new file mode 100644 index 0000000..c2af0db --- /dev/null +++ b/sahara/main/templates/main/home.html @@ -0,0 +1,63 @@ +{% extends 'base.html' %} + +{% block content %} + + + + + + Login Form + + + + +
+
+

Login

+
+ {% csrf_token %} +
+ + +
+
+ + +
+
+
+ + +
+ Forgot password? +
+ +
+
+
+ + + + + +{% endblock %} \ No newline at end of file diff --git a/sahara/main/templates/main/login.html b/sahara/main/templates/main/login.html new file mode 100644 index 0000000..c2af0db --- /dev/null +++ b/sahara/main/templates/main/login.html @@ -0,0 +1,63 @@ +{% extends 'base.html' %} + +{% block content %} + + + + + + Login Form + + + + +
+
+

Login

+
+ {% csrf_token %} +
+ + +
+
+ + +
+
+
+ + +
+ Forgot password? +
+ +
+
+
+ + + + + +{% endblock %} \ No newline at end of file diff --git a/sahara/main/templates/main/mail/welcome_email.html b/sahara/main/templates/main/mail/welcome_email.html deleted file mode 100644 index 9e0c961..0000000 --- a/sahara/main/templates/main/mail/welcome_email.html +++ /dev/null @@ -1,65 +0,0 @@ -{% load static %} - - - - - - - Welcome to Sahara - - - -
-

Welcome to Sahara!

- -

Hello {{ user.first_name }},

-

Thank you for signing up with Sahara! We're excited to have you on board.

-

Please click the button below to activate your account:

- Activate Your Account -

If you did not sign up for this account, please ignore this email.

-

Best regards,
Sahara Team

-
- - diff --git a/sahara/main/templates/main/mail/welcome_email.txt b/sahara/main/templates/main/mail/welcome_email.txt deleted file mode 100644 index 4d772dc..0000000 --- a/sahara/main/templates/main/mail/welcome_email.txt +++ /dev/null @@ -1,14 +0,0 @@ -Welcome to Sahara! - -Hello {{ user.first_name }}, - -Thank you for signing up with Sahara! We're excited to have you on board. - -Please click the link below to activate your account: - -{{ activation_link }} - -If you did not sign up for this account, please ignore this email. - -Best regards, -Sahara Team diff --git a/sahara/main/templates/main/register.html b/sahara/main/templates/main/register.html new file mode 100644 index 0000000..32a26b4 --- /dev/null +++ b/sahara/main/templates/main/register.html @@ -0,0 +1,36 @@ +{% extends 'base.html' %} + +{% block content %} +
+

Register

+
+ {% csrf_token %} +
+ + {{ form.first_name }} +
+
+ + {{ form.last_name }} +
+
+ + {{ form.username }} +
+
+ + {{ form.email }} +
+
+ + {{ form.password }} +
+
+ + {{ form.confirm_password }} +
+ +
+

Already have an account? Login here.

+
+{% endblock %} \ No newline at end of file diff --git a/sahara/main/templates/main/update_user.html b/sahara/main/templates/main/update_user.html deleted file mode 100644 index ad40f5f..0000000 --- a/sahara/main/templates/main/update_user.html +++ /dev/null @@ -1,5 +0,0 @@ -
- {% csrf_token %} - {{ form.as_p }} - -
\ No newline at end of file diff --git a/sahara/main/templates/main/user_list.html b/sahara/main/templates/main/user_list.html deleted file mode 100644 index f534d95..0000000 --- a/sahara/main/templates/main/user_list.html +++ /dev/null @@ -1,5 +0,0 @@ - -{% for user in users %} -

{{ user }} - (delete)

-{% endfor %} \ No newline at end of file diff --git a/sahara/main/templates/main/verification_failed.html b/sahara/main/templates/main/verification_failed.html new file mode 100644 index 0000000..1a909c2 --- /dev/null +++ b/sahara/main/templates/main/verification_failed.html @@ -0,0 +1,10 @@ + + + + Email Verification Failed + + +

Email Verification Failed

+

The verification link is invalid or has expired.

+ + \ No newline at end of file diff --git a/sahara/main/templates/main/verification_success.html b/sahara/main/templates/main/verification_success.html new file mode 100644 index 0000000..2ad2912 --- /dev/null +++ b/sahara/main/templates/main/verification_success.html @@ -0,0 +1,10 @@ + + + + Email Verified + + +

Email Verified Successfully

+

Your email has been verified. You can now log in.

+ + \ No newline at end of file diff --git a/sahara/main/urls.py b/sahara/main/urls.py index 33d5cee..82d2a3a 100644 --- a/sahara/main/urls.py +++ b/sahara/main/urls.py @@ -1,9 +1,11 @@ from django.urls import path -from .views import homeView,send_mail_page,createUserView,activate_account +from . import views urlpatterns = [ - path('',homeView, name="home"), - path('send-email/',send_mail_page,name="sendmail"), - path('user/register/',createUserView,name="register"), - path('activate//', activate_account, name='activate'), -] + path('', views.home, name="home"), + path('register/', views.register, name='register'), + path('activate///', views.activate_account, name='activate_account'), + # path('verify-email///', views.verify_email, name='verify_email'), + path('login/', views.user_login, name='login'), + path('logout/', views.user_logout, name='logout'), +] \ No newline at end of file diff --git a/sahara/main/utilities.py b/sahara/main/utilities.py deleted file mode 100644 index 846fc18..0000000 --- a/sahara/main/utilities.py +++ /dev/null @@ -1,26 +0,0 @@ -from django.core.mail import send_mail -from django.template.loader import render_to_string -from django.utils.html import strip_tags -from django.conf import settings -from django.urls import reverse -from django.contrib.sites.shortcuts import get_current_site - - -def send_welcome_email(user, request): - token = 'generated_token_here' - activation_link = f"{get_current_site(request).domain}{reverse('account:activate', args=[token])}" - subject = "Welcome to Sahara - Please Activate Your Account" - - html_message = render_to_string('welcome_email.html', { - 'user': user, - 'activation_link': activation_link, - }) - plain_message = strip_tags(html_message) - - send_mail( - subject, - plain_message, - settings.DEFAULT_FROM_EMAIL, - [user.email], - html_message=html_message, - ) diff --git a/sahara/main/views.py b/sahara/main/views.py index 94cd1dc..8a07b40 100644 --- a/sahara/main/views.py +++ b/sahara/main/views.py @@ -1,63 +1,82 @@ -from django.shortcuts import render,redirect,HttpResponse -from django.core.mail import send_mail -from django.conf import settings -from django.template.loader import render_to_string -from django.utils.html import strip_tags -from django.conf import settings -from django.urls import reverse -from django.contrib.sites.shortcuts import get_current_site -from . forms import BaseUserForm -from django.contrib import messages +from django.shortcuts import render, redirect,HttpResponse +from django.contrib.auth import login, logout, authenticate from django.contrib.auth.tokens import default_token_generator -# Create your views here. +from django.utils.http import urlsafe_base64_decode +from django.contrib.auth.decorators import login_required +from django.contrib import messages +from .models import User +from .forms import UserRegistrationForm, UserLoginForm +import logging -from .models import BaseUser -def homeView(request): - return HttpResponse("home") +logger = logging.getLogger('main') +def home(request): + return render(request,"main/home.html") -def createUserView(request): + +def register(request): if request.method == 'POST': - form = BaseUserForm(request.POST, request.FILES) - print(form) + form = UserRegistrationForm(request.POST) if form.is_valid(): - form.save() - form.is_a - #return redirect('home') - return HttpResponse("

User Created

") - else: - form = BaseUserForm() - return render(request, 'main/create_user.html', { - 'form': form - }) + user = form.save(commit=False) + user.set_password(form.cleaned_data['password']) + user.save() + try: + user.send_verification_email() + messages.success(request, 'Registration successful! Please check your email to activate your account.') + except Exception as e: + logger.error(f"Error sending verification email: {e}") + messages.error(request, 'Error sending verification email. Please try again later.') -def send_mail_page(): - address = "sajeshmaan@gmail.com" - subject = "Congrts Team Flash" - message = "Congrts Team Flash hackx committe has announced your team as a winner" - try: - send_mail(subject, message, settings.EMAIL_HOST_USER, [address,'rajakiaryal0@gmail.com']) - result= 'Email sent successfully' - except Exception as e: - result = f'Error sending email: {e}' + return redirect('login') + else: + form = UserRegistrationForm() + return render(request, 'main/register.html', {'form': form}) +def activate_account(request, uidb64, token): + try: + uid = urlsafe_base64_decode(uidb64).decode() + user = User.objects.get(pk=uid) + except (TypeError, ValueError, OverflowError, User.DoesNotExist): + user = None + if user is not None and default_token_generator.check_token(user, token): + user.is_active = True + user.save() + messages.success(request, 'Your account has been activated. You can now log in.') + return redirect('login') + else: + messages.error(request, 'Invalid activation link.') + return redirect('register') +def user_login(request): + print(request.user.is_authenticated) + if request.user.is_authenticated: + return redirect('home') -def activate_account(request,token): - return - try: - # Decode token and retrieve user - user = BaseUser.objects.get(email__iexact="") - if user: - # Activate user account - user.is_active = True - user.save() - messages.success(request, "Account activated successfully!") - return redirect('login') # Redirect to the login page + if request.method == 'POST': + email = request.POST['email'] + password = request.POST['password'] + user = authenticate(request, email = email, password = password) + if user is not None: + form = login(request, user) + messages.success(request, f' welcome {user} !!') + return redirect('home') else: - messages.error(request, "Invalid activation link.") - except Exception as e: - messages.error(request, str(e)) - return redirect('home') # Redirect to homepage in case of error + messages.info(request, f'account done not exit plz sign in') + + messages.info(request, f'You are Already Logged in as {request.user}') + return render(request, 'main/login.html', {'title':'log in'}) + + +@login_required +def user_logout(request): + logout(request) + messages.success(request, 'You have been logged out.') + return redirect('login') + +# def verify_email(request, uidb64, token): +# # This view is redundant if it does the same as activate_account +# # Consider removing or assigning a different purpose +# pass \ No newline at end of file diff --git a/sahara/media/Screenshot_1.png b/sahara/media/Screenshot_1.png deleted file mode 100644 index d9c93c6..0000000 Binary files a/sahara/media/Screenshot_1.png and /dev/null differ diff --git a/sahara/media/Screenshot_1_ADxCcNz.png b/sahara/media/Screenshot_1_ADxCcNz.png deleted file mode 100644 index d9c93c6..0000000 Binary files a/sahara/media/Screenshot_1_ADxCcNz.png and /dev/null differ diff --git a/sahara/media/Screenshot_1_Nr3wRlz.png b/sahara/media/Screenshot_1_Nr3wRlz.png deleted file mode 100644 index d9c93c6..0000000 Binary files a/sahara/media/Screenshot_1_Nr3wRlz.png and /dev/null differ diff --git a/sahara/media/Screenshot_1_Y0GUvRC.png b/sahara/media/Screenshot_1_Y0GUvRC.png deleted file mode 100644 index d9c93c6..0000000 Binary files a/sahara/media/Screenshot_1_Y0GUvRC.png and /dev/null differ diff --git a/sahara/media/Screenshot_2025-01-10_182515.png b/sahara/media/Screenshot_2025-01-10_182515.png deleted file mode 100644 index 8e36cba..0000000 Binary files a/sahara/media/Screenshot_2025-01-10_182515.png and /dev/null differ diff --git a/sahara/media/Screenshot_2025-01-10_182515_KdukpRJ.png b/sahara/media/Screenshot_2025-01-10_182515_KdukpRJ.png deleted file mode 100644 index 8e36cba..0000000 Binary files a/sahara/media/Screenshot_2025-01-10_182515_KdukpRJ.png and /dev/null differ diff --git a/sahara/media/uploads/citizenship/Screenshot_1.png b/sahara/media/uploads/citizenship/Screenshot_1.png deleted file mode 100644 index d9c93c6..0000000 Binary files a/sahara/media/uploads/citizenship/Screenshot_1.png and /dev/null differ diff --git a/sahara/sahara/__pycache__/__init__.cpython-312.pyc b/sahara/sahara/__pycache__/__init__.cpython-312.pyc index 1f0e050..439ef61 100644 Binary files a/sahara/sahara/__pycache__/__init__.cpython-312.pyc and b/sahara/sahara/__pycache__/__init__.cpython-312.pyc differ diff --git a/sahara/sahara/__pycache__/settings.cpython-312.pyc b/sahara/sahara/__pycache__/settings.cpython-312.pyc index d2a2fff..566c17c 100644 Binary files a/sahara/sahara/__pycache__/settings.cpython-312.pyc and b/sahara/sahara/__pycache__/settings.cpython-312.pyc differ diff --git a/sahara/sahara/__pycache__/urls.cpython-312.pyc b/sahara/sahara/__pycache__/urls.cpython-312.pyc index 3031018..aa562e2 100644 Binary files a/sahara/sahara/__pycache__/urls.cpython-312.pyc and b/sahara/sahara/__pycache__/urls.cpython-312.pyc differ diff --git a/sahara/sahara/__pycache__/wsgi.cpython-312.pyc b/sahara/sahara/__pycache__/wsgi.cpython-312.pyc index 13a12a8..d30b70c 100644 Binary files a/sahara/sahara/__pycache__/wsgi.cpython-312.pyc and b/sahara/sahara/__pycache__/wsgi.cpython-312.pyc differ diff --git a/sahara/sahara/asgi.py b/sahara/sahara/asgi.py index 93f3e37..1274027 100644 --- a/sahara/sahara/asgi.py +++ b/sahara/sahara/asgi.py @@ -4,7 +4,7 @@ ASGI config for sahara project. It exposes the ASGI callable as a module-level variable named ``application``. For more information on this file, see -https://docs.djangoproject.com/en/5.0/howto/deployment/asgi/ +https://docs.djangoproject.com/en/5.1/howto/deployment/asgi/ """ import os diff --git a/sahara/sahara/settings.py b/sahara/sahara/settings.py index 775d1d4..59b1e40 100644 --- a/sahara/sahara/settings.py +++ b/sahara/sahara/settings.py @@ -1,37 +1,33 @@ """ Django settings for sahara project. -Generated by 'django-admin startproject' using Django 5.0.6. +Generated by 'django-admin startproject' using Django 5.1.4. For more information on this file, see -https://docs.djangoproject.com/en/5.0/topics/settings/ +https://docs.djangoproject.com/en/5.1/topics/settings/ For the full list of settings and their values, see -https://docs.djangoproject.com/en/5.0/ref/settings/ +https://docs.djangoproject.com/en/5.1/ref/settings/ """ from pathlib import Path import os from dotenv import load_dotenv -# Build paths inside the project like this: BASE_DIR / 'subdir'. - -load_dotenv() - # Build paths inside the project like this: BASE_DIR / 'subdir'. BASE_DIR = Path(__file__).resolve().parent.parent -import os - +load_dotenv() # Quick-start development settings - unsuitable for production -# See https://docs.djangoproject.com/en/5.0/howto/deployment/checklist/ +# See https://docs.djangoproject.com/en/5.1/howto/deployment/checklist/ # SECURITY WARNING: keep the secret key used in production secret! -SECRET_KEY = 'django-insecure-dw78ln%=0i^uy4zj78lkx=p_98ik3%h@u%wod1va651^1jyy*m' +SECRET_KEY = 'django-insecure-a#y#*r@98v*f)e!83s5dk9it704i)l7#x-e%59=_%ts%&4lt^2' # SECURITY WARNING: don't run with debug turned on in production! DEBUG = True ALLOWED_HOSTS = ["*"] +FRONTEND_URL = 'http://localhost:8000' # Application definition @@ -61,7 +57,7 @@ ROOT_URLCONF = 'sahara.urls' TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', - 'DIRS': [], + 'DIRS': [BASE_DIR / "templates/"], 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ @@ -76,19 +72,9 @@ TEMPLATES = [ WSGI_APPLICATION = 'sahara.wsgi.application' -####EMAIL CONF - -EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend' -EMAIL_HOST = 'smtp.gmail.com' -EMAIL_USE_TLS = True -EMAIL_PORT = 587 -EMAIL_HOST_USER = os.environ.get("EMAIL_ADDRESS") -EMAIL_HOST_PASSWORD = os.environ.get("EMAIL_PASSWORD") - - # Database -# https://docs.djangoproject.com/en/5.0/ref/settings/#databases +# https://docs.djangoproject.com/en/5.1/ref/settings/#databases DATABASES = { 'default': { @@ -99,7 +85,7 @@ DATABASES = { # Password validation -# https://docs.djangoproject.com/en/5.0/ref/settings/#auth-password-validators +# https://docs.djangoproject.com/en/5.1/ref/settings/#auth-password-validators AUTH_PASSWORD_VALIDATORS = [ { @@ -118,7 +104,7 @@ AUTH_PASSWORD_VALIDATORS = [ # Internationalization -# https://docs.djangoproject.com/en/5.0/topics/i18n/ +# https://docs.djangoproject.com/en/5.1/topics/i18n/ LANGUAGE_CODE = 'en-us' @@ -130,7 +116,23 @@ USE_TZ = True # Static files (CSS, JavaScript, Images) -# https://docs.djangoproject.com/en/5.0/howto/static-files/ +# https://docs.djangoproject.com/en/5.1/howto/static-files/ + +STATIC_URL = 'static/' + +# Default primary key field type +# https://docs.djangoproject.com/en/5.1/ref/settings/#default-auto-field + +DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' + +####EMAIL CONF + +EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend' +EMAIL_HOST = 'smtp.gmail.com' +EMAIL_USE_TLS = True +EMAIL_PORT = 587 +EMAIL_HOST_USER = os.environ.get("EMAIL_ADDRESS") +EMAIL_HOST_PASSWORD = os.environ.get("EMAIL_PASSWORD") STATIC_URL = 'static/' @@ -140,10 +142,10 @@ STATIC_URL = 'static/' DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' -AUTH_USER_MODEL = 'main.BaseUser' +AUTH_USER_MODEL = 'main.User' STATIC_URL = '/static/' -#STATICFILES_DIRS = [BASE_DIR / "static"] STATIC_ROOT = os.path.join(BASE_DIR, 'static') MEDIA_URL = '/media/' -MEDIA_ROOT = os.path.join(BASE_DIR, 'media') \ No newline at end of file +MEDIA_ROOT = os.path.join(BASE_DIR, 'media') + diff --git a/sahara/sahara/urls.py b/sahara/sahara/urls.py index e33b46c..83e71c3 100644 --- a/sahara/sahara/urls.py +++ b/sahara/sahara/urls.py @@ -1,12 +1,7 @@ from django.contrib import admin -from django.urls import path,include -from django.conf import settings -from django.conf.urls.static import static +from django.urls import path, include urlpatterns = [ path('admin/', admin.site.urls), - path('',include("main.urls")) -] - -if settings.DEBUG: - urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) \ No newline at end of file + path('', include('main.urls')), +] \ No newline at end of file diff --git a/sahara/sahara/wsgi.py b/sahara/sahara/wsgi.py index 40d30f0..04cccfb 100644 --- a/sahara/sahara/wsgi.py +++ b/sahara/sahara/wsgi.py @@ -4,7 +4,7 @@ WSGI config for sahara project. It exposes the WSGI callable as a module-level variable named ``application``. For more information on this file, see -https://docs.djangoproject.com/en/5.0/howto/deployment/wsgi/ +https://docs.djangoproject.com/en/5.1/howto/deployment/wsgi/ """ import os diff --git a/sahara/templates/base.html b/sahara/templates/base.html new file mode 100644 index 0000000..6afacda --- /dev/null +++ b/sahara/templates/base.html @@ -0,0 +1,38 @@ + + + + + + Sahara + + + + + + + {% block content %} + + {% endblock %} \ No newline at end of file