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:
@@ -87,6 +87,13 @@ class AppSettings(Base):
|
||||
# take it (base64url, uncompressed P-256 point).
|
||||
vapid_public: Mapped[str] = mapped_column(Text, nullable=False, server_default=text("''"))
|
||||
vapid_private_ct: Mapped[bytes | None] = mapped_column(BYTEA)
|
||||
# Users may invite friends (app/identity/invitations.py); the invitation
|
||||
# email's From address (empty: postmaster@APP_DOMAIN), subject and HTML
|
||||
# template (empty: the built-in ones).
|
||||
invitations_enabled: Mapped[bool] = mapped_column(nullable=False, server_default=text("false"))
|
||||
invite_from: Mapped[str] = mapped_column(Text, nullable=False, server_default=text("''"))
|
||||
invite_subject: Mapped[str] = mapped_column(Text, nullable=False, server_default=text("''"))
|
||||
invite_template: Mapped[str] = mapped_column(Text, nullable=False, server_default=text("''"))
|
||||
updated_at: Mapped[dt.datetime] = _now()
|
||||
|
||||
|
||||
@@ -376,6 +383,27 @@ class UserAppPassword(Base):
|
||||
server_id: Mapped[str | None] = mapped_column(Text)
|
||||
|
||||
|
||||
class Invitation(Base):
|
||||
"""A single-use link that lets one person sign up, sent by a user."""
|
||||
|
||||
__tablename__ = "invitations"
|
||||
|
||||
id: Mapped[uuid.UUID] = _uuid_pk()
|
||||
inviter_id: Mapped[uuid.UUID] = mapped_column(
|
||||
UUID(as_uuid=True), ForeignKey("users.id", ondelete="CASCADE"), nullable=False, index=True
|
||||
)
|
||||
# sha256 of the token in the link; the token itself is only in the email.
|
||||
token_hash: Mapped[str] = mapped_column(Text, nullable=False, unique=True)
|
||||
invitee_name: Mapped[str] = mapped_column(Text, nullable=False)
|
||||
invitee_email: Mapped[str] = mapped_column(Text, nullable=False)
|
||||
created_at: Mapped[dt.datetime] = _now()
|
||||
expires_at: Mapped[dt.datetime] = mapped_column(TIMESTAMP(timezone=True), nullable=False)
|
||||
used_at: Mapped[dt.datetime | None] = mapped_column(TIMESTAMP(timezone=True))
|
||||
used_by: Mapped[uuid.UUID | None] = mapped_column(
|
||||
UUID(as_uuid=True), ForeignKey("users.id", ondelete="SET NULL")
|
||||
)
|
||||
|
||||
|
||||
class PushSubscription(Base):
|
||||
"""A browser or installed app to tell about new mail (Web Push)."""
|
||||
|
||||
|
||||
Reference in New Issue
Block a user