diff --git a/django_project/django_project/settings.py b/django_project/django_project/settings.py index b76dde59c48a1b3a2fb233f295f26d9827172fbe..6260cbced2f92b344065d49fb2a6d41dd22a8abe 100644 --- a/django_project/django_project/settings.py +++ b/django_project/django_project/settings.py @@ -31,6 +31,18 @@ DEBUG = True ALLOWED_HOSTS = [] +# Define STATIC_URL +STATIC_URL = '/static/' + +# Define STATIC_ROOT (where `collectstatic` will place files) +STATIC_ROOT = os.path.join(BASE_DIR, "staticfiles") + +# Ensure static files directories +STATICFILES_DIRS = [] +static_dir = os.path.join(BASE_DIR, "static") +if os.path.exists(static_dir): + STATICFILES_DIRS.append(static_dir) # If you have a `static/` folder inside your project + # Application definition diff --git a/django_project/myapp/entrypoint.sh b/django_project/myapp/entrypoint.sh index 9de6c62fa7af228190b3414d8c809e3f9898a575..f7636a57b63d1d466513fc0697af01160e4ba61a 100755 --- a/django_project/myapp/entrypoint.sh +++ b/django_project/myapp/entrypoint.sh @@ -7,5 +7,8 @@ python manage.py migrate --noinput # Run the custom database initialization command python manage.py initdb +# Collect static files (important for CSRF in some cases) +python manage.py collectstatic --noinput + # Start the Django development server (or your production server) exec python manage.py runserver 0.0.0.0:8000 diff --git a/django_project/myapp/templates/myapp/edit_account.html b/django_project/myapp/templates/myapp/edit_account.html index 6d94253663279a30a643ac7814a8c456179b3aab..162f3ecc56f875d778d5fc299fa8a00954935738 100644 --- a/django_project/myapp/templates/myapp/edit_account.html +++ b/django_project/myapp/templates/myapp/edit_account.html @@ -30,34 +30,37 @@ <form method="POST" enctype="multipart/form-data"> {% csrf_token %} - + + {% if error %} + <p style="color: red;">{{ error }}</p> + {% endif %} + <label for="name">Name:</label> - <input type="text" name="name" id="name" value="{{ user.name }}" required> - + <input type="text" name="name" value="{{ form_data.name|default_if_none:'' }}" required> + <label for="email">Email:</label> - <input type="email" name="email" id="email" value="{{ user.email }}" required> - + <input type="email" name="email" value="{{ form_data.email|default_if_none:'' }}" required> + + <label for="password">Password:</label> + <input type="password" name="password" required> + <label for="username">Username:</label> - <input type="text" name="username" id="username" value="{{ user.username }}" required> - + <input type="text" name="username" value="{{ form_data.username|default_if_none:'' }}" required> + <label for="studentid">Student ID:</label> - <input type="number" name="studentid" id="studentid" value="{{ user.studentid }}" required> - + <input type="number" name="studentid" value="{{ form_data.studentid|default_if_none:'' }}" required> + <label for="startyear">Start Year:</label> - <input type="number" name="startyear" id="startyear" value="{{ user.startyear }}" required> - + <input type="number" name="startyear" value="{{ form_data.startyear|default_if_none:'' }}" required> + <label for="endyear">End Year:</label> - <input type="number" name="endyear" id="endyear" value="{{ user.endyear }}" required> - + <input type="number" name="endyear" value="{{ form_data.endyear|default_if_none:'' }}" required> + <label for="profilepicture">Profile Picture:</label> - {% if user.profilepicture %} - <img src="{{ user.profilepicture }}" alt="Profile Picture" class="profile-pic"> - {% endif %} - <input type="file" name="profilepicture" id="profilepicture"> - - <br><br> - <button type="submit">Update Account</button> - </form> + <input type="file" name="profilepicture"> + + <button type="submit">Register</button> +</form> </body> </html> diff --git a/django_project/myapp/views/userviews.py b/django_project/myapp/views/userviews.py index 539c3e562e32a8c68bf68dbffe14d1d4597f5d6e..f9ad56e0a2b56a2f342a1c9d1d6d3e32a862012d 100644 --- a/django_project/myapp/views/userviews.py +++ b/django_project/myapp/views/userviews.py @@ -9,89 +9,97 @@ db_url = settings.DATABASE_URL def register_view(request): """ - Handles user registration. Expects POST with + Handles user registration. Expects POST with: 'name', 'email', 'password', 'username', 'studentid', 'startyear', 'endyear', and optionally 'profilepicture'. """ - db_url = settings.DATABASE_URL # Make sure DATABASE_URL is defined in your Django settings - context = {} + db_url = settings.DATABASE_URL + context = {"form_data": {}} # Store form data and errors if request.method == 'POST': - name = request.POST.get('name', '').strip() - email = request.POST.get('email', '').strip() - password = request.POST.get('password', '').strip() - username = request.POST.get('username', '').strip() - studentid_str = request.POST.get('studentid', '').strip() - startyear_str = request.POST.get('startyear', '').strip() - endyear_str = request.POST.get('endyear', '').strip() - - - # 1. Check required fields - if not all([name, email, password, username, studentid_str, startyear_str, endyear_str]): - context['error'] = ( - "Name, email, username, student ID, start year, and end year are required." - ) - return render(request, 'myapp/register.html', context) - - # 2. Try converting studentid, startyear, endyear to int - try: - studentid = int(studentid_str) - except ValueError: - context['error'] = "Student ID must be a number." - return render(request, 'myapp/register.html', context) - - try: - startyear = int(startyear_str) - except ValueError: - context['error'] = "Start year must be a number." - return render(request, 'myapp/register.html', context) - + # Retrieve user input + form_data = { + "name": request.POST.get("name", "").strip(), + "email": request.POST.get("email", "").strip(), + "password": request.POST.get("password", "").strip(), + "username": request.POST.get("username", "").strip(), + "studentid": request.POST.get("studentid", "").strip(), + "startyear": request.POST.get("startyear", "").strip(), + "endyear": request.POST.get("endyear", "").strip(), + } + + # Preserve form data for repopulation + context["form_data"] = form_data + + # 1. Check Required Fields + if not all(form_data.values()): + context["error"] = "All fields are required." + return render(request, "myapp/register.html", context) + + # 2. Convert Numeric Fields with Error Handling try: - endyear = int(endyear_str) + form_data["studentid"] = int(form_data["studentid"]) + form_data["startyear"] = int(form_data["startyear"]) + form_data["endyear"] = int(form_data["endyear"]) except ValueError: - context['error'] = "End year must be a number." - return render(request, 'myapp/register.html', context) + context["error"] = "Student ID, Start Year, and End Year must be numbers." + return render(request, "myapp/register.html", context) - # 3. Handle profile picture upload (if any) - profilepicture_file = request.FILES.get('profilepicture') - saved_path = None # Default to None if no file is uploaded + # 3. Handle Profile Picture Upload (if provided) + profilepicture_file = request.FILES.get("profilepicture") + saved_path = None if profilepicture_file: - # Make sure your form <input> matches name="profilepicture" - filename = profilepicture_file.name - - # Decide a directory inside MEDIA_ROOT for profile pics - profile_dir = os.path.join(settings.MEDIA_ROOT, 'profile_pics') - os.makedirs(profile_dir, exist_ok=True) # Ensure the folder exists + try: + profile_dir = os.path.join(settings.MEDIA_ROOT, "profile_pics") + os.makedirs(profile_dir, exist_ok=True) # Ensure directory exists - # Build the full filesystem path where we’ll store the file - save_path = os.path.join(profile_dir, filename) + filename = profilepicture_file.name + save_path = os.path.join(profile_dir, filename) - # Write the file to disk in chunks - with open(save_path, 'wb+') as destination: - for chunk in profilepicture_file.chunks(): - destination.write(chunk) + with open(save_path, "wb+") as destination: + for chunk in profilepicture_file.chunks(): + destination.write(chunk) - # The path we'll store in the DB (so we can serve it at /media/profile_pics/<filename>) - saved_path = f"/media/profile_pics/{filename}" + # Store relative media path in DB + saved_path = f"/media/profile_pics/{filename}" + except Exception as e: + context["error"] = f"Error saving profile picture: {str(e)}" + return render(request, "myapp/register.html", context) - # Debug prints to confirm we have the file and path + # Debugging Information print("DEBUG: profilepicture_file is:", profilepicture_file) print("DEBUG: saved_path is:", saved_path) - # 4. Call Rust to create the user + # 4. Call Rust API to Create User try: - rust_crud_api.create_user( db_url,name, email, password, username, studentid, startyear, endyear, saved_path, True) - # 5. After successful registration, redirect to login or some other page - return redirect('login') + rust_crud_api.create_user( + db_url, + form_data["name"], + form_data["email"], + form_data["password"], + form_data["username"], + form_data["studentid"], + form_data["startyear"], + form_data["endyear"], + saved_path, + True, # First-time login flag + ) + return redirect("login") # Redirect to login page after success except Exception as e: - context['error'] = f"An error occurred: {str(e)}" - return render(request, 'myapp/register.html', context) - - # If GET or anything else, just render the registration form - return render(request, 'myapp/register.html', context) + error_message = str(e) + + # Handle PostgreSQL unique constraint violation (Student ID exists) + if "Student ID already exists" in error_message: + context["error"] = "A user with this Student ID already exists. Please choose a different ID." + else: + context["error"] = f"An error occurred: {error_message}" + print("DEBUG: Rust API Error:", error_message) # Log error for debugging + return render(request, "myapp/register.html", context) + # Render registration form for GET requests + return render(request, "myapp/register.html", context) def account_view(request): """ diff --git a/docker-compose.yml b/docker-compose.yml index 98072c1ae5f07639b790c65512568563e5d8aaed..2a246a779dc8a3c2143812fcab490e204a825a03 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,4 +1,3 @@ -version: '3.9' services: db: image: postgres:14 @@ -25,11 +24,15 @@ services: condition: service_healthy volumes: - ./media:/app/media + - static_volume:/app/staticfiles # ✅ Make sure this volume exists ports: - "8000:8000" environment: DATABASE_URL: "postgres://postgres:postgres@db:5432/postgres" + DEBUG: "True" + ALLOWED_HOSTS: "localhost,127.0.0.1,0.0.0.0" + CSRF_TRUSTED_ORIGINS: "http://localhost:8000, http://127.0.0.1:8000" volumes: - pgdata: {} - + pgdata: {} # ✅ Keeps PostgreSQL data persistent + static_volume: {} # ✅ Defines the missing static volume