From 2b4d18a2228f92f2b9525b787b854f2c096fd992 Mon Sep 17 00:00:00 2001 From: James Ravenscroft Date: Sun, 24 Nov 2024 07:45:02 +0000 Subject: [PATCH] implement custom user --- penparse/penparse/settings.py | 4 + penparse/penparse/urls.py | 1 + penparse/webui/auth.py | 15 ++++ penparse/webui/migrations/0001_initial.py | 44 +++++++++++ penparse/webui/models.py | 6 +- penparse/webui/templates/index.html | 61 +++++++++++++++ penparse/webui/templates/main.html | 22 ++++++ penparse/webui/templates/partial/nav.html | 14 ++++ penparse/webui/templates/register.html | 91 +++++++++++++++++++++++ penparse/webui/urls.py | 1 + penparse/webui/views.py | 48 +++++++++++- 11 files changed, 303 insertions(+), 4 deletions(-) create mode 100644 penparse/webui/auth.py create mode 100644 penparse/webui/migrations/0001_initial.py create mode 100644 penparse/webui/templates/index.html create mode 100644 penparse/webui/templates/main.html create mode 100644 penparse/webui/templates/partial/nav.html create mode 100644 penparse/webui/templates/register.html diff --git a/penparse/penparse/settings.py b/penparse/penparse/settings.py index c1c2e48..7800186 100644 --- a/penparse/penparse/settings.py +++ b/penparse/penparse/settings.py @@ -101,6 +101,10 @@ AUTH_PASSWORD_VALIDATORS = [ ] +AUTH_USER_MODEL = 'webui.User' + +AUTHENTICATION_BACKENDS = ['webui.auth.EmailBackend'] + # Internationalization # https://docs.djangoproject.com/en/4.2/topics/i18n/ diff --git a/penparse/penparse/urls.py b/penparse/penparse/urls.py index 711e4bf..2843830 100644 --- a/penparse/penparse/urls.py +++ b/penparse/penparse/urls.py @@ -21,5 +21,6 @@ from django.urls import path, include urlpatterns = [ path("", include("webui.urls")), path('admin/', admin.site.urls), + path("accounts/", include("django.contrib.auth.urls")), ] diff --git a/penparse/webui/auth.py b/penparse/webui/auth.py new file mode 100644 index 0000000..b50574d --- /dev/null +++ b/penparse/webui/auth.py @@ -0,0 +1,15 @@ +from django.contrib.auth import get_user_model +from django.contrib.auth.backends import ModelBackend + + +class EmailBackend(ModelBackend): + def authenticate(self, request, username=None, password=None, **kwargs): + UserModel = get_user_model() + try: + user = UserModel.objects.get(email=username) + except UserModel.DoesNotExist: + return None + else: + if user.check_password(password): # type: ignore + return user + return None diff --git a/penparse/webui/migrations/0001_initial.py b/penparse/webui/migrations/0001_initial.py new file mode 100644 index 0000000..b9f933c --- /dev/null +++ b/penparse/webui/migrations/0001_initial.py @@ -0,0 +1,44 @@ +# Generated by Django 4.2.16 on 2024-11-23 13:03 + +import django.contrib.auth.models +import django.contrib.auth.validators +from django.db import migrations, models +import django.utils.timezone + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('auth', '0012_alter_user_first_name_max_length'), + ] + + operations = [ + migrations.CreateModel( + name='User', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('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')), + ('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')), + ('email', models.EmailField(max_length=254, unique=True)), + ('password', models.CharField(blank=True, max_length=256)), + ('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', + 'abstract': False, + }, + managers=[ + ('objects', django.contrib.auth.models.UserManager()), + ], + ), + ] diff --git a/penparse/webui/models.py b/penparse/webui/models.py index 71a8362..a49cae3 100644 --- a/penparse/webui/models.py +++ b/penparse/webui/models.py @@ -1,3 +1,7 @@ +from django.contrib.auth.models import AbstractUser from django.db import models -# Create your models here. + +class User(AbstractUser): + email = models.EmailField(unique=True) + password = models.CharField(max_length=256, blank=True) diff --git a/penparse/webui/templates/index.html b/penparse/webui/templates/index.html new file mode 100644 index 0000000..b5e927a --- /dev/null +++ b/penparse/webui/templates/index.html @@ -0,0 +1,61 @@ +{% extends "main.html" %} {% block content %} +
+

+ Transform Handwritten Notes into Digital Text +

+

+ AnnoMemo uses intelligent OCR to digitize your handwritten notes and + integrate them with your favorite PKM tools. +

+ Get Started +
+ +
+

Features

+
+
+

Intelligent OCR

+

+ Accurately convert handwritten notes to digital text +

+
+
+

PKM Integration

+

+ Seamlessly integrate with Obsidian, Memos, and Joplin +

+
+
+

Bot Support

+

Process photos via Telegram and Discord bots

+
+
+
+ +
+

Integrations

+
+ Obsidian + Memos + Joplin + Telegram + Discord +
+
+ +
+

Simple Pricing

+

+ Start for free, upgrade when you need more +

+ View Pricing Plans +
+{% endblock %} diff --git a/penparse/webui/templates/main.html b/penparse/webui/templates/main.html new file mode 100644 index 0000000..4a127bf --- /dev/null +++ b/penparse/webui/templates/main.html @@ -0,0 +1,22 @@ + + + + + + AnnoMemo - Intelligent OCR for Handwritten Notes + + + +
{% include "partial/nav.html" %}
+ +
+ {%block content%} {%endblock%} +
+ + + + diff --git a/penparse/webui/templates/partial/nav.html b/penparse/webui/templates/partial/nav.html new file mode 100644 index 0000000..5e9e6a7 --- /dev/null +++ b/penparse/webui/templates/partial/nav.html @@ -0,0 +1,14 @@ + diff --git a/penparse/webui/templates/register.html b/penparse/webui/templates/register.html new file mode 100644 index 0000000..e6ded5e --- /dev/null +++ b/penparse/webui/templates/register.html @@ -0,0 +1,91 @@ +{% extends "main.html" %} {% block content %} +
+
+
+

+ Create Your AnnoMemo Account +

+ {% if messages %} {% for message in messages %} +
+ {{ message }} +
+ {% endfor %} {% endif %} +
+ {% csrf_token %} +
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ +
+
+
+
+

+ Already have an account? + Sign in +

+
+
+
+{% endblock %} diff --git a/penparse/webui/urls.py b/penparse/webui/urls.py index 5119061..91a7007 100644 --- a/penparse/webui/urls.py +++ b/penparse/webui/urls.py @@ -4,4 +4,5 @@ from . import views urlpatterns = [ path("", views.index, name="index"), + path("register", views.register, name="register"), ] diff --git a/penparse/webui/views.py b/penparse/webui/views.py index 1252696..036b73e 100644 --- a/penparse/webui/views.py +++ b/penparse/webui/views.py @@ -1,6 +1,48 @@ -from django.shortcuts import render -from django.http import HttpResponse +from django.contrib import messages +from django.shortcuts import redirect, render +from django.http import HttpRequest, HttpResponse + +from .models import User def index(request): - return HttpResponse("Hello, world. You're at the polls index.") + # return HttpResponse("Hello, world. You're at the polls index.") + return render(request, 'index.html') + + +def register(request: HttpRequest): + + # if the form is not submitted yet, return the form + if request.method != 'POST': + return render(request, 'register.html', {'errors': False}) + + email = request.POST.get('email') + password = request.POST.get('password') + confirm_password = request.POST.get('confirm_password') + + errors = False + + if not email: + messages.error(request, 'Email is required') + errors = True + + if not password or len(password) < 8: + messages.error(request, 'Password must be at least 8 characters long') + errors = True + + if password != confirm_password: + messages.error(request, 'Passwords do not match') + errors = True + + if not errors: + + if User.objects.filter(email=email).exists(): + messages.error(request, 'Email already exists') + else: + user = User.objects.create_user( + username=username, email=email, password=password) # type: ignore + user.save() + messages.success(request, 'Account created successfully') + return redirect('login') + + return render(request, 'register.html')