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 %}
+
+ AnnoMemo uses intelligent OCR to digitize your handwritten notes and
+ integrate them with your favorite PKM tools.
+
+ Accurately convert handwritten notes to digital text
+
+ Seamlessly integrate with Obsidian, Memos, and Joplin
+ Process photos via Telegram and Discord bots
+ Start for free, upgrade when you need more
+
+ Transform Handwritten Notes into Digital Text
+
+ Features
+ Intelligent OCR
+ PKM Integration
+ Bot Support
+ Integrations
+
+
+
+
+
+
Simple Pricing
+
+ Already have an account? + Sign in +
+