From 34601f019e2b42f5af1c802c49038e90c9d130ef Mon Sep 17 00:00:00 2001 From: James2Tulloch <146088090+James2Tulloch@users.noreply.github.com> Date: Fri, 21 Feb 2025 11:12:03 +0000 Subject: [PATCH] Working basic user registration --- Dockerfile | 52 +++++++----- django_project/myapp/.DS_Store | Bin 6148 -> 6148 bytes django_project/myapp/entrypoint.sh | 11 +++ .../myapp/management/commands/initdb.py | 17 ++++ .../myapp/templates/myapp/login.html | 22 +++-- .../myapp/templates/myapp/register.html | 28 +++++-- django_project/myapp/views.py | 75 ++++++++++++------ 7 files changed, 145 insertions(+), 60 deletions(-) create mode 100755 django_project/myapp/entrypoint.sh create mode 100644 django_project/myapp/management/commands/initdb.py diff --git a/Dockerfile b/Dockerfile index 162d11b..f99ad09 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,57 +1,67 @@ # Stage 1: Build the Rust extension using maturin FROM rust:latest as builder -# Install Python and system dependencies required for maturin +# Install Python, venv, and build dependencies RUN apt-get update && apt-get install -y \ python3 \ - python3-pip \ + python3-venv \ + python3-dev \ build-essential \ - libssl-dev + libssl-dev && \ + rm -rf /var/lib/apt/lists/* + +# Create and activate a virtual environment +RUN python3 -m venv /venv +ENV PATH="/venv/bin:$PATH" + +# Upgrade pip (no need for --break-system-packages in a venv) +RUN pip install --upgrade pip # Install maturin -RUN pip3 install --break-system-packages maturin +RUN pip install maturin WORKDIR /build -# Copy the Rust project folder into the builder stage. -# This assumes the folder "rust_crud_api" is in the build context. +# Copy the rust_crud_api project folder into the builder stage COPY rust_crud_api/ ./rust_crud_api/ -# Change directory into the Rust project and build the wheel. +# Change into the rust_crud_api directory and build the wheel WORKDIR /build/rust_crud_api RUN maturin build --release # Stage 2: Build the final Django container FROM python:3.11-slim -# Install system dependencies needed for Django and Postgres -RUN apt-get update && apt-get install -y libpq-dev gcc && rm -rf /var/lib/apt/lists/* +# Install system dependencies for Django and PostgreSQL client +RUN apt-get update && apt-get install -y \ + libpq-dev \ + gcc && \ + rm -rf /var/lib/apt/lists/* WORKDIR /app -# Copy the built Rust extension wheel from the builder stage. -# This assumes maturin built the wheel into rust_crud_api/target/wheels/ +# Copy the built wheel from the builder stage COPY --from=builder /build/rust_crud_api/target/wheels/*.whl . -# Install the Rust extension into the Python environment. +# Install the Rust extension RUN pip install --no-cache-dir *.whl -# Copy your Django project into the container. -# In this example, we assume the Django project (including manage.py) is inside the "django_project" folder. +# Copy the Django project into the container COPY django_project/ ./django_project/ -# Set the working directory to your Django project folder. +# Change working directory to the Django project folder WORKDIR /app/django_project -# Install Django and other Python dependencies (adjust as needed). +# Install Django and the PostgreSQL adapter RUN pip install --no-cache-dir django psycopg2-binary -# Set environment variables (adjust the DATABASE_URL as needed). -ENV DATABASE_URL=postgres://postgres:postgres@db:5432/postgres +# Copy the entrypoint script from your app directory and ensure it's executable +COPY django_project/myapp/entrypoint.sh ./myapp/entrypoint.sh +RUN chmod +x ./myapp/entrypoint.sh -# Expose the port that Django will run on. +# Expose the port Django will run on EXPOSE 8000 -# Run the Django development server (for production use a proper WSGI/ASGI server). -CMD ["python", "manage.py", "runserver", "0.0.0.0:8000"] +# Run the entrypoint script (which should run migrations, init the database, and start the server) +CMD ["/app/django_project/myapp/entrypoint.sh"] diff --git a/django_project/myapp/.DS_Store b/django_project/myapp/.DS_Store index 98099036d71ed48c5219617645a892d80e5a4bae..674394648b1cfb241b58e57b2016a69134322d6d 100644 GIT binary patch delta 266 zcmZoMXfc@J&&aniU^g=(-((&Z4L(L$1_lPb|6sttFxiPkS-cxrgn@y9ogtB-fT3Wr z1G{+rPc%7RhE#?;h7yJ%hDwG4hJ1!hFi(%6m?6V6CqFqUCqIdSfkA+Qfe~ctJg_zY z89;!GA(tVMA&()EA)O(WAs1o}njVnbQ1nc`!6LGG2a5yaWGyz8$uewwObqKc>#%7t jN-;PxFfbsi<zOgd$Ye-mD4*=WF2=*K9wEJngGB%U8Ddj0 delta 184 zcmZoMXfc@J&&abeU^g=(&tx7J4H<^T3=9kt|3d)-0|N&`B0~y8E<+|m-ed(9QE?## z28Q`i>HiE24D1Yv3<V4YlLOeq_!t`)7#Oy~BscT1MlkX*1T!!&OooX~ZeSOi+{MN> zIgL$a^By)1Mk&Vk3=9mrVe()L%NQ~lQW?r82e66rMlmoj%z?>HEG*v4&heKY01b~# A9{>OV diff --git a/django_project/myapp/entrypoint.sh b/django_project/myapp/entrypoint.sh new file mode 100755 index 0000000..9de6c62 --- /dev/null +++ b/django_project/myapp/entrypoint.sh @@ -0,0 +1,11 @@ +#!/bin/sh +set -e + +# Apply database migrations +python manage.py migrate --noinput + +# Run the custom database initialization command +python manage.py initdb + +# 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/management/commands/initdb.py b/django_project/myapp/management/commands/initdb.py new file mode 100644 index 0000000..8ceabcf --- /dev/null +++ b/django_project/myapp/management/commands/initdb.py @@ -0,0 +1,17 @@ +# myapp/management/commands/initdb.py + +from django.core.management.base import BaseCommand +from django.conf import settings +import rust_crud_api + +class Command(BaseCommand): + help = 'Initializes the database by creating necessary tables via the Rust extension' + + def handle(self, *args, **options): + db_url = settings.DATABASE_URL + try: + rust_crud_api.init_db(db_url) + self.stdout.write(self.style.SUCCESS('Database initialized successfully.')) + except Exception as e: + self.stderr.write(self.style.ERROR(f'Error initializing database: {e}')) + diff --git a/django_project/myapp/templates/myapp/login.html b/django_project/myapp/templates/myapp/login.html index 36e1ee9..1b2e6f2 100644 --- a/django_project/myapp/templates/myapp/login.html +++ b/django_project/myapp/templates/myapp/login.html @@ -1,21 +1,29 @@ <!DOCTYPE html> -<html> +<html lang="en"> <head> + <meta charset="UTF-8"> <title>Login</title> + <style> + body { font-family: Arial, sans-serif; margin: 2em; } + .error { color: red; } + </style> </head> <body> <h1>Login</h1> - {% if messages %} - {% for message in messages %} - <p>{{ message }}</p> - {% endfor %} + {% if error %} + <p class="error">{{ error }}</p> {% endif %} <form method="post"> {% csrf_token %} - {{ form.as_p }} + <label for="email">Email:</label> + <input type="email" name="email" id="email" required> + <br><br> <button type="submit">Login</button> </form> - <p>Don't have an account? <a href="{% url 'register' %}">Register here</a>.</p> + <p> + Don't have an account? + <a href="{% url 'register' %}">Register here</a> + </p> </body> </html> diff --git a/django_project/myapp/templates/myapp/register.html b/django_project/myapp/templates/myapp/register.html index 35a9694..97e890f 100644 --- a/django_project/myapp/templates/myapp/register.html +++ b/django_project/myapp/templates/myapp/register.html @@ -1,21 +1,35 @@ <!DOCTYPE html> -<html> +<html lang="en"> <head> + <meta charset="UTF-8"> <title>Register</title> + <style> + body { font-family: Arial, sans-serif; margin: 2em; } + .error { color: red; } + </style> </head> <body> <h1>Register</h1> - {% if messages %} - {% for message in messages %} - <p>{{ message }}</p> - {% endfor %} + + {% if error %} + <p class="error">{{ error }}</p> {% endif %} + <form method="post"> {% csrf_token %} - {{ form.as_p }} + <label for="id_name">Name:</label> + <input type="text" name="name" id="id_name" required><br><br> + + <label for="id_email">Email:</label> + <input type="email" name="email" id="id_email" required><br><br> + <button type="submit">Register</button> </form> - <p>Already have an account? <a href="{% url 'login' %}">Login here</a>.</p> + + <p> + Already have an account? + <a href="{% url 'login' %}">Login here</a>. + </p> </body> </html> diff --git a/django_project/myapp/views.py b/django_project/myapp/views.py index a29ed48..63c9eba 100644 --- a/django_project/myapp/views.py +++ b/django_project/myapp/views.py @@ -16,59 +16,84 @@ def init_db_view(request): def register_view(request): """ - Register a new user using the Rust extension. - Expects POST with 'name' and 'email'. - After registration, it simulates a login by storing the user's info in session. + Handle user registration: + - Display the registration form on GET. + - On POST, create a new user via the Rust extension. + - If successful, simulate login by storing the user in the session, + then redirect to an account page. """ - db_url = settings.DATABASE_URL + db_url = settings.DATABASE_URL # Make sure this is defined in settings.py + context = {} + if request.method == 'POST': name = request.POST.get('name') email = request.POST.get('email') + if not name or not email: - return JsonResponse({'error': 'Name and email are required.'}, status=400) + context['error'] = "Both name and email are required." + return render(request, 'myapp/register.html', context) + try: - # Create user in the database via the Rust extension. + # Create the user via the Rust extension. rust_crud_api.create_user(db_url, name, email) - # Since our extension does not return the new user’s id directly, - # we retrieve all users and find the one with the matching email. + + # Optionally, simulate login by retrieving all users and finding the new one. + # (In a production system, you'd have proper password handling.) users = rust_crud_api.get_all_users(db_url) - user = next((u for u in users if u.email == email), None) + user = next((u for u in users if u.email.lower() == email.lower()), None) + if user is None: - return JsonResponse({'error': 'Registration failed.'}, status=500) - # Simulate login by storing user info in session. + context['error'] = "Registration failed. Please try again." + return render(request, 'myapp/register.html', context) + + # Store user information in the session to simulate login. request.session['user_id'] = user.id - request.session['user_email'] = user.email request.session['user_name'] = user.name + request.session['user_email'] = user.email + return redirect('account') except Exception as e: - return JsonResponse({'error': str(e)}, status=500) - return render(request, 'myapp/register.html') - + context['error'] = f"An error occurred: {str(e)}" + return render(request, 'myapp/register.html', context) + + # For GET requests, just display the registration form. + return render(request, 'myapp/register.html', context) def login_view(request): """ - Login a user by looking up their email in the database. - (Since our Rust extension doesn't handle passwords, we simply check if the email exists.) + Handles user login by checking for the user's email in the database using the Rust extension. + If a matching user is found, their info is stored in the session. """ - db_url = settings.DATABASE_URL + db_url = settings.DATABASE_URL # This should be defined in your settings.py + context = {} + if request.method == 'POST': email = request.POST.get('email') if not email: - return JsonResponse({'error': 'Email is required.'}, status=400) + context['error'] = "Email is required." + return render(request, 'myapp/login.html', context) + try: - # Retrieve all users and try to find one with the given email. + # Retrieve all users from the database via the Rust extension users = rust_crud_api.get_all_users(db_url) - user = next((u for u in users if u.email == email), None) + # Find a user with the matching email + user = next((u for u in users if u.email.lower() == email.lower()), None) if user is None: - return JsonResponse({'error': 'User not found.'}, status=404) - # Simulate login by storing user info in session. + context['error'] = "User not found. Please register first." + return render(request, 'myapp/login.html', context) + + # Simulate a login by storing user information in the session. request.session['user_id'] = user.id request.session['user_email'] = user.email request.session['user_name'] = user.name + + # Redirect to the account page or another dashboard. return redirect('account') except Exception as e: - return JsonResponse({'error': str(e)}, status=500) - return render(request, 'myapp/login.html') + context['error'] = f"An error occurred: {str(e)}" + return render(request, 'myapp/login.html', context) + + return render(request, 'myapp/login.html', context) def account_view(request): -- GitLab