diff --git a/forum/investors/migrations/0001_initial.py b/forum/investors/migrations/0001_initial.py deleted file mode 100644 index afbf191..0000000 --- a/forum/investors/migrations/0001_initial.py +++ /dev/null @@ -1,30 +0,0 @@ -# Generated by Django 4.2.22 on 2025-06-05 18:33 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - initial = True - - dependencies = [ - ] - - operations = [ - migrations.CreateModel( - name='InvestorProfile', - fields=[ - ('investor_id', models.AutoField(primary_key=True, serialize=False)), - ('company_name', models.CharField(max_length=255)), - ('industry', models.CharField(blank=True, max_length=255, null=True)), - ('created_at', models.DateField(auto_now_add=True)), - ('address', models.CharField(max_length=255)), - ('website', models.URLField(blank=True, null=True)), - ], - options={ - 'verbose_name': 'investor', - 'verbose_name_plural': 'investors', - 'db_table': 'investors', - }, - ), - ] diff --git a/forum/investors/migrations/0002_initial.py b/forum/investors/migrations/0002_initial.py deleted file mode 100644 index 928e154..0000000 --- a/forum/investors/migrations/0002_initial.py +++ /dev/null @@ -1,23 +0,0 @@ -# Generated by Django 4.2.22 on 2025-06-05 18:33 - -from django.conf import settings -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - initial = True - - dependencies = [ - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ('investors', '0001_initial'), - ] - - operations = [ - migrations.AddField( - model_name='investorprofile', - name='user_id', - field=models.OneToOneField(db_column='user_id', on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL), - ), - ] diff --git a/forum/projects/migrations/0001_initial.py b/forum/projects/migrations/0001_initial.py deleted file mode 100644 index 9a6afb3..0000000 --- a/forum/projects/migrations/0001_initial.py +++ /dev/null @@ -1,32 +0,0 @@ -# Generated by Django 4.2.22 on 2025-06-05 18:33 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - initial = True - - dependencies = [ - ('investors', '0001_initial'), - ] - - operations = [ - migrations.CreateModel( - name='Project', - fields=[ - ('project_id', models.AutoField(primary_key=True, serialize=False)), - ('title', models.CharField(max_length=255)), - ('description', models.TextField(blank=True, null=True)), - ('progress', models.BooleanField(default=False)), - ('created_at', models.DateTimeField(auto_now_add=True)), - ('updated_at', models.DateTimeField(auto_now=True)), - ('investor', models.ManyToManyField(to='investors.investorprofile')), - ], - options={ - 'verbose_name': 'project', - 'verbose_name_plural': 'projects', - 'db_table': 'projects', - }, - ), - ] diff --git a/forum/projects/migrations/0002_initial.py b/forum/projects/migrations/0002_initial.py deleted file mode 100644 index 9ba1821..0000000 --- a/forum/projects/migrations/0002_initial.py +++ /dev/null @@ -1,22 +0,0 @@ -# Generated by Django 4.2.22 on 2025-06-05 18:33 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - initial = True - - dependencies = [ - ('projects', '0001_initial'), - ('startups', '0001_initial'), - ] - - operations = [ - migrations.AddField( - model_name='project', - name='startup', - field=models.ForeignKey(db_column='startup_id', on_delete=django.db.models.deletion.CASCADE, to='startups.startupprofile'), - ), - ] diff --git a/forum/users/migrations/0001_initial.py b/forum/users/migrations/0001_initial.py deleted file mode 100644 index cfa6741..0000000 --- a/forum/users/migrations/0001_initial.py +++ /dev/null @@ -1,44 +0,0 @@ -# Generated by Django 4.2.22 on 2025-06-05 18:33 - -import django.core.validators -from django.db import migrations, models -import phonenumber_field.modelfields - - -class Migration(migrations.Migration): - - initial = True - - dependencies = [ - ('auth', '0012_alter_user_first_name_max_length'), - ] - - operations = [ - migrations.CreateModel( - name='User', - fields=[ - ('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')), - ('user_id', models.AutoField(primary_key=True, serialize=False)), - ('email', models.EmailField(max_length=254, unique=True)), - ('password', models.CharField(max_length=128, validators=[django.core.validators.MinLengthValidator(8)])), - ('first_name', models.CharField(blank=True, default='', max_length=255)), - ('last_name', models.CharField(blank=True, default='', max_length=255)), - ('user_name', models.CharField(max_length=255)), - ('title', models.CharField(blank=True, default='', max_length=255)), - ('user_phone', phonenumber_field.modelfields.PhoneNumberField(blank=True, default='', max_length=128, region=None)), - ('is_staff', models.BooleanField(default=False)), - ('is_active', models.BooleanField(default=True)), - ('is_superuser', models.BooleanField(default=False)), - ('role', models.PositiveSmallIntegerField(choices=[(1, 'Investor'), (2, 'Startup')], default=1)), - ('created_at', models.DateTimeField(auto_now_add=True)), - ('updated_at', models.DateTimeField(auto_now=True)), - ('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': 'user', - 'verbose_name_plural': 'users', - 'db_table': 'users', - }, - ), - ] diff --git a/forum/users/serializers.py b/forum/users/serializers.py index 8bcde87..c4107c5 100644 --- a/forum/users/serializers.py +++ b/forum/users/serializers.py @@ -1,6 +1,7 @@ from rest_framework import serializers from django.contrib.auth.password_validation import validate_password from .models import User +from rest_framework_simplejwt.tokens import RefreshToken, TokenError class UserRegisterSerializer(serializers.ModelSerializer): password = serializers.CharField( @@ -31,4 +32,23 @@ def create(self, validated_data): class UserResponseSerializer(serializers.ModelSerializer): class Meta: model = User - fields = ['email', 'user_name'] \ No newline at end of file + fields = ['email', 'user_name'] + + + +class LogoutSerializer(serializers.Serializer): + refresh = serializers.CharField() + + default_error_messages = { + "bad_token": "Incorrect or already revoked token." + } + + def validate(self, attrs): + self.token = attrs["refresh"] + return attrs + + def save(self, **kwargs): + try: + RefreshToken(self.token).blacklist() + except TokenError: + self.fail("bad_token") diff --git a/forum/users/urls.py b/forum/users/urls.py index cc875a5..541c80b 100644 --- a/forum/users/urls.py +++ b/forum/users/urls.py @@ -1,10 +1,11 @@ from django.urls import path from rest_framework_simplejwt.views import TokenObtainPairView, TokenRefreshView -from .views import RegisterView +from .views import RegisterView, LogoutView urlpatterns = [ path('register/', RegisterView.as_view(), name='register'), path('login/', TokenObtainPairView.as_view(), name='token_obtain_pair'), path('refresh/', TokenRefreshView.as_view(), name='token_refresh'), + path("logout/", LogoutView.as_view(), name="auth_logout"), ] \ No newline at end of file diff --git a/forum/users/views.py b/forum/users/views.py index c6b11df..74fc2db 100644 --- a/forum/users/views.py +++ b/forum/users/views.py @@ -1,8 +1,10 @@ from rest_framework.views import APIView from rest_framework.response import Response from rest_framework import status -from .serializers import UserRegisterSerializer +from .serializers import UserRegisterSerializer,LogoutSerializer import logging +from rest_framework.permissions import IsAuthenticated + logger = logging.getLogger(__name__) @@ -18,3 +20,14 @@ def post(self, request): logger.warning(f"User registration failed. Errors: {serializer.errors}") return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) + + + +class LogoutView(APIView): + permission_classes = [IsAuthenticated] + + def post(self, request): + serializer = LogoutSerializer(data=request.data) + serializer.is_valid(raise_exception=True) + serializer.save() + return Response({"detail": "Logout successful."}, status=status.HTTP_204_NO_CONTENT) \ No newline at end of file