- Extract comment templates into reusable partials (_comment.html, _comment_form.html, _comment_list.html, _reply_form.html, etc.) - Add HTMX progressive enhancement: inline form submission with partial responses, delta polling for live updates, form reset on success, success/moderation toast feedback - Integrate Cloudflare Turnstile for invisible bot protection: server-side token validation with hostname check, fail-closed on errors/timeouts, feature-flagged via TURNSTILE_SECRET_KEY env var - Auto-approve comments that pass Turnstile; keep manual approval as fallback when Turnstile is disabled (model default stays False) - Add CommentReaction model with UniqueConstraint for session-based anonymous reactions (heart/thumbs-up), toggle support, separate rate-limit bucket (20/min) - Add comment poll endpoint (GET /comments/poll/<id>/?after_id=N) for HTMX delta polling without duplicates - Update CSP middleware to allow challenges.cloudflare.com in script-src, connect-src, and frame-src - Self-host htmx.min.js (v2.0.4) to minimize CSP surface area - Add django-htmx middleware and requests to dependencies - Add Unapprove bulk action to Wagtail admin for moderation - Extend PII purge command to anonymize reaction session_key - Design refresh: neon glow avatars, solid hover shadows, gradient section header, cyan reply borders, grid-pattern empty state, neon-pink focus glow on form inputs - Add turnstile_site_key to template context via context processor - 18 new tests covering HTMX contracts, Turnstile success/failure/ timeout/hostname-mismatch, polling deltas, reaction toggle/dedup/ rate-limit, CSP headers, and PII purge extension Closes #43 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
28 lines
1.1 KiB
Python
28 lines
1.1 KiB
Python
# Generated by Django 5.2.11 on 2026-03-03 22:49
|
|
|
|
import django.db.models.deletion
|
|
from django.db import migrations, models
|
|
|
|
|
|
class Migration(migrations.Migration):
|
|
|
|
dependencies = [
|
|
('comments', '0001_initial'),
|
|
]
|
|
|
|
operations = [
|
|
migrations.CreateModel(
|
|
name='CommentReaction',
|
|
fields=[
|
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
|
('reaction_type', models.CharField(choices=[('heart', '❤️'), ('plus_one', '👍')], max_length=20)),
|
|
('session_key', models.CharField(max_length=64)),
|
|
('created_at', models.DateTimeField(auto_now_add=True)),
|
|
('comment', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='reactions', to='comments.comment')),
|
|
],
|
|
options={
|
|
'constraints': [models.UniqueConstraint(fields=('comment', 'reaction_type', 'session_key'), name='unique_comment_reaction_per_session')],
|
|
},
|
|
),
|
|
]
|