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:
@@ -12,6 +12,9 @@ class RegisterRequest(BaseModel):
|
||||
localpart: str = Field(min_length=3, max_length=30)
|
||||
password: str = Field(min_length=8, max_length=128)
|
||||
recovery_email: str | None = None
|
||||
# A friend's invitation (app/identity/invitations.py): signs up even while
|
||||
# public registration is closed, and is used up by it.
|
||||
invite: str | None = Field(default=None, max_length=200)
|
||||
|
||||
|
||||
class LoginRequest(BaseModel):
|
||||
@@ -98,23 +101,39 @@ class UserOut(BaseModel):
|
||||
mail_host: str | None = None
|
||||
|
||||
|
||||
class InvitationCheckIn(BaseModel):
|
||||
token: str = Field(min_length=1, max_length=200)
|
||||
|
||||
|
||||
class InvitationCheckOut(BaseModel):
|
||||
valid: bool
|
||||
invitee_name: str = ""
|
||||
inviter_name: str = ""
|
||||
|
||||
|
||||
class RegistrationStatusOut(BaseModel):
|
||||
"""Public, unauthenticated: what the signup page should show."""
|
||||
|
||||
open: bool
|
||||
# True when the instance has no users yet, so signup creates the administrator.
|
||||
bootstrap: bool
|
||||
# The domain every handle lives under (localpart@domain), shown beside the
|
||||
# handle field.
|
||||
domain: str
|
||||
|
||||
|
||||
class AdminSettingsOut(BaseModel):
|
||||
registration_open: bool
|
||||
default_quota_bytes: int
|
||||
# Users may send friends sign-up invitations (app/identity/invitations.py).
|
||||
invitations_enabled: bool
|
||||
|
||||
|
||||
class AdminSettingsIn(BaseModel):
|
||||
"""Merge semantics: omitted fields are left as they are."""
|
||||
|
||||
registration_open: bool | None = None
|
||||
invitations_enabled: bool | None = None
|
||||
# Applies to users created after the change; existing mailboxes keep theirs.
|
||||
default_quota_bytes: int | None = Field(default=None, gt=0)
|
||||
|
||||
@@ -145,6 +164,36 @@ class AdminMailIn(BaseModel):
|
||||
public_url: str | None = Field(default=None, max_length=500)
|
||||
|
||||
|
||||
class AdminInviteTemplateOut(BaseModel):
|
||||
# Empty strings: the built-in subject / template / sender is used.
|
||||
subject: str
|
||||
html: str
|
||||
from_address: str
|
||||
default_subject: str
|
||||
default_html: str
|
||||
default_from: str
|
||||
placeholders: list[str]
|
||||
|
||||
|
||||
class AdminInviteTemplateIn(BaseModel):
|
||||
"""Merge semantics: omitted fields are left as they are; an empty string
|
||||
goes back to the built-in one."""
|
||||
|
||||
subject: str | None = Field(default=None, max_length=300)
|
||||
html: str | None = None
|
||||
from_address: str | None = Field(default=None, max_length=320)
|
||||
|
||||
|
||||
class AdminInvitePreviewIn(BaseModel):
|
||||
subject: str = Field(default="", max_length=300)
|
||||
html: str = ""
|
||||
|
||||
|
||||
class AdminInvitePreviewOut(BaseModel):
|
||||
subject: str
|
||||
html: str
|
||||
|
||||
|
||||
class AdminMailTestIn(BaseModel):
|
||||
to: str = Field(min_length=3, max_length=320)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user