Skip to content

Commit 67decdb

Browse files
committed
feat: completed backend tasks including RBAC, file upload, cleanup command, and frontend updates
1 parent 891e949 commit 67decdb

28 files changed

Lines changed: 1365 additions & 1195 deletions

.gitignore

Lines changed: 30 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,49 @@
1-
# Logs
2-
logs
1+
# LOGS
2+
3+
logs/
34
*.log
45
npm-debug.log*
56
yarn-debug.log*
67
yarn-error.log*
78
pnpm-debug.log*
89
lerna-debug.log*
910

10-
# Editor directories and files
11+
# EDITORS / OS
1112
.vscode/*
1213
!.vscode/extensions.json
13-
.idea
14+
.idea/
1415
.DS_Store
1516
*.suo
1617
*.ntvs*
1718
*.njsproj
1819
*.sln
1920
*.sw?
2021

21-
# Client
22-
client/node_modules
23-
client/dist
24-
client/dist-ssr
25-
client/*.local
22+
# NODE / NEXT.JS
23+
node_modules/
24+
.next/
25+
out/
26+
27+
# PYTHON
28+
venv/
29+
__pycache__/
30+
*.pyc
31+
32+
# ENV FILES
33+
.env
34+
.env.local
35+
36+
# FRONTEND
37+
38+
frontend/node_modules/
39+
frontend/.next/
40+
2641

27-
# Server
28-
server/.env
29-
server/node_modules
30-
server/dist
42+
# BACKEND
43+
backend/venv/
44+
backend/__pycache__/
45+
backend/uploads/
3146

32-
# prompts
47+
# TEMP / NOTES
48+
*.txt
3349
prompts/debug
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# Generated by Django 5.0.2 on 2026-01-29 18:22
2+
3+
import django.db.models.deletion
4+
from django.db import migrations, models
5+
6+
7+
class Migration(migrations.Migration):
8+
dependencies = [
9+
("authentication", "0001_initial"),
10+
("chat", "0004_uploadedfile_uploaded_by"),
11+
]
12+
13+
operations = [
14+
migrations.AddField(
15+
model_name="customuser",
16+
name="role",
17+
field=models.ForeignKey(
18+
blank=True,
19+
null=True,
20+
on_delete=django.db.models.deletion.SET_NULL,
21+
related_name="users",
22+
to="chat.role",
23+
),
24+
),
25+
migrations.AlterField(
26+
model_name="customuser",
27+
name="is_active",
28+
field=models.BooleanField(default=True),
29+
),
30+
]

backend/authentication/models.py

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1-
from django.contrib.auth.models import AbstractBaseUser, BaseUserManager, PermissionsMixin
1+
from django.contrib.auth.models import (
2+
AbstractBaseUser,
3+
BaseUserManager,
4+
PermissionsMixin,
5+
)
26
from django.db import models
37

48

@@ -17,8 +21,8 @@ def create_user(self, email, password, **extra_fields):
1721
email = self.normalize_email(email)
1822
user = self.model(email=email, **extra_fields)
1923
user.set_password(password)
24+
user.is_active = True
2025
user.save(using=self._db)
21-
2226
return user
2327

2428
def create_superuser(self, email, password, **extra_fields):
@@ -31,9 +35,20 @@ def create_superuser(self, email, password, **extra_fields):
3135

3236
class CustomUser(AbstractBaseUser, PermissionsMixin):
3337
email = models.EmailField(unique=True)
34-
is_active = models.BooleanField(default=False)
38+
39+
# Django auth flags
40+
is_active = models.BooleanField(default=True)
3541
is_staff = models.BooleanField(default=False)
3642

43+
# ROLE-BASED ACCESS CONTROL
44+
role = models.ForeignKey(
45+
"chat.Role",
46+
on_delete=models.SET_NULL,
47+
null=True,
48+
blank=True,
49+
related_name="users",
50+
)
51+
3752
objects = CustomUserManager()
3853

3954
USERNAME_FIELD = "email"

backend/authentication/views.py

Lines changed: 61 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -2,78 +2,107 @@
22
from django.contrib.auth import authenticate, login, logout
33
from django.http import JsonResponse
44
from django.middleware.csrf import get_token
5+
from django.views.decorators.csrf import csrf_exempt
6+
57
from rest_framework import status
6-
from rest_framework.decorators import api_view
8+
from rest_framework.decorators import (
9+
api_view,
10+
permission_classes,
11+
authentication_classes,
12+
)
13+
from rest_framework.permissions import AllowAny, IsAuthenticated
714

815
from authentication.models import CustomUser
916

1017

1118
@api_view(["GET"])
19+
@permission_classes([AllowAny])
1220
def auth_root_view(request):
1321
return JsonResponse({"message": "Auth endpoint works!"})
1422

1523

1624
@api_view(["GET"])
25+
@permission_classes([AllowAny])
1726
def csrf_token(request):
18-
token = get_token(request)
19-
return JsonResponse({"data": token})
27+
return JsonResponse({"csrfToken": get_token(request)})
28+
2029

2130

31+
@csrf_exempt
2232
@api_view(["POST"])
33+
@authentication_classes([])
34+
@permission_classes([AllowAny])
2335
def login_view(request):
2436
email = request.data.get("email")
2537
password = request.data.get("password")
2638

27-
try:
28-
user = CustomUser.objects.get(email=email)
29-
except CustomUser.DoesNotExist:
30-
return JsonResponse({"error": "Invalid credentials"}, status=status.HTTP_401_UNAUTHORIZED)
31-
32-
# Check if the user is active
33-
if not user.is_active:
34-
return JsonResponse({"error": "User is not active"}, status=status.HTTP_401_UNAUTHORIZED)
39+
if not email or not password:
40+
return JsonResponse(
41+
{"error": "Email and password required"},
42+
status=status.HTTP_400_BAD_REQUEST,
43+
)
3544

3645
user = authenticate(request, email=email, password=password)
37-
if user is not None:
38-
login(request, user)
39-
response = JsonResponse({"data": "Login successful"})
46+
if not user:
47+
return JsonResponse(
48+
{"error": "Invalid credentials"},
49+
status=status.HTTP_401_UNAUTHORIZED,
50+
)
4051

41-
# Set session cookie manually
42-
session_key = request.session.session_key
43-
session_cookie_name = settings.SESSION_COOKIE_NAME
44-
max_age = settings.SESSION_COOKIE_AGE
45-
response.set_cookie(session_cookie_name, session_key, max_age=max_age, httponly=True)
46-
47-
return response
48-
else:
49-
return JsonResponse({"error": "Invalid credentials"}, status=status.HTTP_401_UNAUTHORIZED)
52+
if not user.is_active:
53+
return JsonResponse(
54+
{"error": "User inactive"},
55+
status=status.HTTP_403_FORBIDDEN,
56+
)
57+
58+
login(request, user)
59+
60+
response = JsonResponse({"message": "Login successful"})
61+
response.set_cookie(
62+
settings.SESSION_COOKIE_NAME,
63+
request.session.session_key,
64+
httponly=True,
65+
)
66+
return response
5067

5168

69+
@csrf_exempt
5270
@api_view(["POST"])
71+
@authentication_classes([])
72+
@permission_classes([AllowAny])
5373
def logout_view(request):
5474
logout(request)
55-
response = JsonResponse({"data": "Logout successful"})
75+
response = JsonResponse({"message": "Logout successful"})
5676
response.delete_cookie(settings.SESSION_COOKIE_NAME)
57-
5877
return response
5978

6079

6180
@api_view(["POST"])
81+
@permission_classes([AllowAny])
6282
def register_view(request):
6383
email = request.data.get("email")
6484
password = request.data.get("password")
85+
6586
if not email or not password:
66-
return JsonResponse({"error": "Email and password are required"}, status=status.HTTP_400_BAD_REQUEST)
87+
return JsonResponse(
88+
{"error": "Email and password required"},
89+
status=status.HTTP_400_BAD_REQUEST,
90+
)
6791

6892
if CustomUser.objects.filter(email=email).exists():
69-
return JsonResponse({"error": "Email is already taken"}, status=status.HTTP_400_BAD_REQUEST)
93+
return JsonResponse(
94+
{"error": "Email already exists"},
95+
status=status.HTTP_400_BAD_REQUEST,
96+
)
7097

71-
CustomUser.objects.create_user(email, password=password)
72-
return JsonResponse({"data": "User created successfully"}, status=status.HTTP_201_CREATED)
98+
CustomUser.objects.create_user(email=email, password=password)
99+
return JsonResponse(
100+
{"message": "User created"},
101+
status=status.HTTP_201_CREATED,
102+
)
73103

74104

75105
@api_view(["GET"])
106+
@permission_classes([IsAuthenticated])
76107
def verify_session(request):
77-
session_cookie = request.COOKIES.get("sessionid")
78-
is_authenticated = request.user.is_authenticated and session_cookie == request.session.session_key
79-
return JsonResponse({"data": is_authenticated})
108+
return JsonResponse({"authenticated": True})

backend/backend/settings.py

Lines changed: 34 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,10 @@
3030
# SECURITY WARNING: don't run with debug turned on in production!
3131
DEBUG = True
3232

33-
ALLOWED_HOSTS = []
34-
33+
ALLOWED_HOSTS = [
34+
"*"
35+
]
3536
# Application definition
36-
3737
INSTALLED_APPS = [
3838
"django.contrib.admin",
3939
"django.contrib.auth",
@@ -44,11 +44,13 @@
4444
"corsheaders",
4545
"rest_framework",
4646
"nested_admin",
47+
"django_crontab",
4748
"authentication",
4849
"chat",
4950
"gpt",
5051
]
5152

53+
5254
MIDDLEWARE = [
5355
"corsheaders.middleware.CorsMiddleware",
5456
"django.middleware.security.SecurityMiddleware",
@@ -57,7 +59,6 @@
5759
"django.middleware.csrf.CsrfViewMiddleware",
5860
"django.contrib.auth.middleware.AuthenticationMiddleware",
5961
"django.contrib.messages.middleware.MessageMiddleware",
60-
"django.middleware.clickjacking.XFrameOptionsMiddleware",
6162
]
6263

6364
ROOT_URLCONF = "backend.urls"
@@ -81,13 +82,14 @@
8182
WSGI_APPLICATION = "backend.wsgi.application"
8283

8384

84-
# Database
85-
# https://docs.djangoproject.com/en/4.2/ref/settings/#databases
86-
8785
DATABASES = {
8886
"default": {
89-
"ENGINE": "django.db.backends.sqlite3",
90-
"NAME": BASE_DIR / "db.sqlite3",
87+
"ENGINE": "django.db.backends.postgresql",
88+
"NAME": "soulpage_db",
89+
"USER": "soulpage_user",
90+
"PASSWORD": "strongpassword",
91+
"HOST": "localhost",
92+
"PORT": "5432",
9193
}
9294
}
9395

@@ -138,14 +140,36 @@
138140
DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
139141

140142
CORS_ALLOWED_ORIGINS = [
141-
FRONTEND_URL,
143+
"http://localhost:3000",
144+
"http://127.0.0.1:3000",
142145
]
146+
143147
CORS_ALLOW_CREDENTIALS = True
144148

149+
145150
CSRF_TRUSTED_ORIGINS = [
146151
FRONTEND_URL,
147152
]
148153

149154
SESSION_COOKIE_SECURE = True
150155
CSRF_COOKIE_SECURE = True
151156
CSRF_COOKIE_SAMESITE = "None"
157+
158+
# BYPASS AUTHENTICATION
159+
REST_FRAMEWORK = {
160+
"DEFAULT_AUTHENTICATION_CLASSES": [
161+
"rest_framework.authentication.BasicAuthentication",
162+
"rest_framework.authentication.SessionAuthentication",
163+
],
164+
}
165+
166+
167+
# Cron jobs
168+
169+
CRONJOBS = [
170+
(
171+
"*/1 * * * *",
172+
"django.core.management.call_command",
173+
["cleanup_conversations"],
174+
),
175+
]

0 commit comments

Comments
 (0)