Based on the git diff provided, here's a concise and descriptive commit message:

feat: add passkey authentication and enhance user preferences

- Add passkey login security event type with fingerprint icon
- Include request and site context in email confirmation for backend
- Add user_id exact match filter to prevent incorrect user lookups
- Enable PATCH method for updating user preferences via API
- Add moderation_preferences support to user settings
- Optimize ticket queries with select_related and prefetch_related

This commit introduces passkey authentication tracking, improves user
profile filtering accuracy, and extends the preferences API to support
updates. Query optimizations reduce database hits for ticket listings.
This commit is contained in:
pacnpal
2026-01-12 19:13:05 -05:00
parent 2b66814d82
commit d631f3183c
56 changed files with 5860 additions and 264 deletions

View File

@@ -0,0 +1,53 @@
# Generated by Django 5.2.10 on 2026-01-10 22:01
import apps.core.choices.fields
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
("support", "0004_alter_ticket_category_alter_ticket_status"),
]
operations = [
migrations.AlterField(
model_name="report",
name="report_type",
field=apps.core.choices.fields.RichChoiceField(
allow_deprecated=False,
choice_group="report_types",
choices=[
("inaccurate", "Inaccurate Information"),
("inappropriate", "Inappropriate Content"),
("spam", "Spam"),
("copyright", "Copyright Violation"),
("duplicate", "Duplicate Content"),
("other", "Other"),
],
db_index=True,
domain="support",
help_text="Type of issue being reported",
max_length=20,
),
),
migrations.AlterField(
model_name="report",
name="status",
field=apps.core.choices.fields.RichChoiceField(
allow_deprecated=False,
choice_group="report_statuses",
choices=[
("pending", "Pending"),
("investigating", "Investigating"),
("resolved", "Resolved"),
("dismissed", "Dismissed"),
],
db_index=True,
default="pending",
domain="support",
help_text="Current status of the report",
max_length=20,
),
),
]

View File

@@ -0,0 +1,147 @@
# Generated by Django 5.2.10 on 2026-01-11 20:42
import apps.core.choices.fields
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("support", "0005_alter_report_report_type_alter_report_status"),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name="EmailThread",
fields=[
("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
("created_at", models.DateTimeField(auto_now_add=True)),
("updated_at", models.DateTimeField(auto_now=True)),
(
"message_id",
models.CharField(blank=True, help_text="Email message ID for threading", max_length=255),
),
("from_email", models.EmailField(help_text="Sender email address", max_length=254)),
("to_email", models.EmailField(help_text="Recipient email address", max_length=254)),
("subject", models.CharField(max_length=255)),
("body_text", models.TextField(help_text="Plain text email body")),
(
"direction",
apps.core.choices.fields.RichChoiceField(
allow_deprecated=False,
choice_group="email_directions",
choices=[("inbound", "Inbound"), ("outbound", "Outbound")],
domain="support",
help_text="Whether email is inbound or outbound",
max_length=10,
),
),
],
options={
"verbose_name": "Email Thread",
"verbose_name_plural": "Email Threads",
"ordering": ["created_at"],
"abstract": False,
},
),
migrations.AddField(
model_name="ticket",
name="admin_notes",
field=models.TextField(blank=True, help_text="Internal notes for administrators"),
),
migrations.AddField(
model_name="ticket",
name="archived_at",
field=models.DateTimeField(blank=True, help_text="When the ticket was archived", null=True),
),
migrations.AddField(
model_name="ticket",
name="archived_by",
field=models.ForeignKey(
blank=True,
help_text="Staff member who archived this ticket",
null=True,
on_delete=django.db.models.deletion.SET_NULL,
related_name="archived_tickets",
to=settings.AUTH_USER_MODEL,
),
),
migrations.AddField(
model_name="ticket",
name="assigned_to",
field=models.ForeignKey(
blank=True,
help_text="Staff member assigned to this ticket",
null=True,
on_delete=django.db.models.deletion.SET_NULL,
related_name="assigned_tickets",
to=settings.AUTH_USER_MODEL,
),
),
migrations.AddField(
model_name="ticket",
name="name",
field=models.CharField(
blank=True, help_text="Name of the submitter (for anonymous tickets)", max_length=255
),
),
migrations.AddField(
model_name="ticket",
name="resolved_at",
field=models.DateTimeField(blank=True, help_text="When the ticket was resolved", null=True),
),
migrations.AddField(
model_name="ticket",
name="resolved_by",
field=models.ForeignKey(
blank=True,
help_text="Staff member who resolved this ticket",
null=True,
on_delete=django.db.models.deletion.SET_NULL,
related_name="resolved_tickets",
to=settings.AUTH_USER_MODEL,
),
),
migrations.AddField(
model_name="ticket",
name="ticket_number",
field=models.CharField(blank=True, help_text="Human-readable ticket number", max_length=20, unique=True),
),
migrations.AddIndex(
model_name="ticket",
index=models.Index(fields=["status", "created_at"], name="support_tic_status_d0b46e_idx"),
),
migrations.AddIndex(
model_name="ticket",
index=models.Index(fields=["ticket_number"], name="support_tic_ticket__d87f40_idx"),
),
migrations.AddIndex(
model_name="ticket",
index=models.Index(fields=["archived_at"], name="support_tic_archive_8fe8c5_idx"),
),
migrations.AddField(
model_name="emailthread",
name="sent_by",
field=models.ForeignKey(
blank=True,
help_text="Staff member who sent this email (for outbound)",
null=True,
on_delete=django.db.models.deletion.SET_NULL,
related_name="sent_email_threads",
to=settings.AUTH_USER_MODEL,
),
),
migrations.AddField(
model_name="emailthread",
name="ticket",
field=models.ForeignKey(
help_text="Associated support ticket",
on_delete=django.db.models.deletion.CASCADE,
related_name="email_threads",
to="support.ticket",
),
),
]

View File

@@ -0,0 +1,27 @@
# Generated by Django 5.2.10 on 2026-01-11 21:19
import apps.core.state_machine.fields
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
("support", "0006_add_ticket_admin_fields_and_email_threads"),
]
operations = [
migrations.AlterField(
model_name="ticket",
name="status",
field=apps.core.state_machine.fields.RichFSMField(
allow_deprecated=False,
choice_group="ticket_statuses",
choices=[("open", "Open"), ("in_progress", "In Progress"), ("closed", "Closed")],
db_index=True,
default="open",
domain="support",
max_length=20,
),
),
]