feat: email unsubscribe tokens with one-click opt-out
- Migration 0019: email_unsubscribe_token column on users (unique, indexed)
- Token auto-generated on first email address save (same pattern as RSS token)
- GET /api/notifications/unsubscribe/{token} — no auth required, sets
email_enabled=False and returns a branded HTML confirmation page
- List-Unsubscribe + List-Unsubscribe-Post headers on every email
(improves deliverability; enables one-click unsubscribe in Gmail/Outlook)
- Unsubscribe link appended to email body plain text
Authored by: Jack Levy
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
22
backend/alembic/versions/0019_add_email_unsubscribe_token.py
Normal file
22
backend/alembic/versions/0019_add_email_unsubscribe_token.py
Normal file
@@ -0,0 +1,22 @@
|
||||
"""Add email_unsubscribe_token to users
|
||||
|
||||
Revision ID: 0019
|
||||
Revises: 0018
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
revision = "0019"
|
||||
down_revision = "0018"
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
op.add_column("users", sa.Column("email_unsubscribe_token", sa.String(64), nullable=True))
|
||||
op.create_index("ix_users_email_unsubscribe_token", "users", ["email_unsubscribe_token"], unique=True)
|
||||
|
||||
|
||||
def downgrade():
|
||||
op.drop_index("ix_users_email_unsubscribe_token", table_name="users")
|
||||
op.drop_column("users", "email_unsubscribe_token")
|
||||
Reference in New Issue
Block a user