Invite a friend; handle field shows the domain; system mail fixes
- Invitations: each user can invite two friends by email (sign-up works through the link even while registration is closed); admin-editable template, subject and sender. - Invitation sender defaults to the system-mail From address, which the SMTP account is allowed to send as (was postmaster@APP_DOMAIN, refused with 553 when the two differ). - Sign-in, sign-up and forgot-password fields show the fixed @domain after the typed name; the registration status reports it. - System mail gets a Date header; the invitation's plain-text part keeps its paragraphs and line breaks. - SMTP EHLO uses SMTP_HELO_NAME / APP_DOMAIN, not the container name. - DNS recommendations for the mail domains. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,63 @@
|
||||
"""invitations: users invite friends, even while sign-up is closed
|
||||
|
||||
An administrator switch lets every user send a few single-use invitation links
|
||||
by email; a link opens sign-up for the person it was sent to even when public
|
||||
registration is off. The email is built from a template the administrator
|
||||
uploads (or the built-in one) and sent from the instance's own address.
|
||||
|
||||
Revision ID: 0027
|
||||
Revises: 0026
|
||||
Create Date: 2026-10-14
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Sequence
|
||||
|
||||
from alembic import op
|
||||
|
||||
revision: str = "0027"
|
||||
down_revision: str | None = "0026"
|
||||
branch_labels: str | Sequence[str] | None = None
|
||||
depends_on: str | Sequence[str] | None = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
op.execute(
|
||||
"""
|
||||
ALTER TABLE app_settings
|
||||
ADD COLUMN invitations_enabled boolean NOT NULL DEFAULT false,
|
||||
ADD COLUMN invite_from text NOT NULL DEFAULT '',
|
||||
ADD COLUMN invite_subject text NOT NULL DEFAULT '',
|
||||
ADD COLUMN invite_template text NOT NULL DEFAULT ''
|
||||
"""
|
||||
)
|
||||
op.execute(
|
||||
"""
|
||||
CREATE TABLE invitations (
|
||||
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
inviter_id uuid NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||
token_hash text NOT NULL UNIQUE,
|
||||
invitee_name text NOT NULL,
|
||||
invitee_email text NOT NULL,
|
||||
created_at timestamptz NOT NULL DEFAULT now(),
|
||||
expires_at timestamptz NOT NULL,
|
||||
used_at timestamptz,
|
||||
used_by uuid REFERENCES users(id) ON DELETE SET NULL
|
||||
)
|
||||
"""
|
||||
)
|
||||
op.execute("CREATE INDEX ix_invitations_inviter ON invitations (inviter_id)")
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
op.execute("DROP TABLE IF EXISTS invitations")
|
||||
op.execute(
|
||||
"""
|
||||
ALTER TABLE app_settings
|
||||
DROP COLUMN IF EXISTS invitations_enabled,
|
||||
DROP COLUMN IF EXISTS invite_from,
|
||||
DROP COLUMN IF EXISTS invite_subject,
|
||||
DROP COLUMN IF EXISTS invite_template
|
||||
"""
|
||||
)
|
||||
Reference in New Issue
Block a user