File size: 2,559 Bytes
00a4363
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
import enum
import uuid
from datetime import datetime

from sqlalchemy import DateTime, Enum, ForeignKey, Integer, String, Text, UniqueConstraint, Uuid
from sqlalchemy.orm import Mapped, mapped_column

from app.db import Base
from app.models.base import IdMixin, TimestampMixin


class VerificationPurpose(str, enum.Enum):
    SIGNUP = "signup"
    PASSWORD_RESET = "password_reset"


class EmailVerificationCode(IdMixin, TimestampMixin, Base):
    __tablename__ = "email_verification_codes"

    email: Mapped[str] = mapped_column(String(320), index=True)
    purpose: Mapped[VerificationPurpose] = mapped_column(
        Enum(VerificationPurpose, native_enum=False, length=20)
    )
    code_hash: Mapped[str] = mapped_column(String(128))
    # Signup stores the pending name + password hash until the code is verified.
    pending_name: Mapped[str | None] = mapped_column(String(200))
    pending_password_hash: Mapped[str | None] = mapped_column(String(200))
    attempts: Mapped[int] = mapped_column(Integer, default=0)
    expires_at: Mapped[datetime] = mapped_column(DateTime(timezone=True))
    consumed_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True))


class UpdateEmail(IdMixin, TimestampMixin, Base):
    """Broadcast campaign. Body is plain text (it gets HTML escaped)."""

    __tablename__ = "update_emails"

    subject: Mapped[str] = mapped_column(String(300))
    body: Mapped[str] = mapped_column(Text)
    user_filter: Mapped[str] = mapped_column(String(20), default="all")  # all | free | paid | writer | studio
    batch_size: Mapped[int] = mapped_column(Integer, default=50)
    send_hour: Mapped[int] = mapped_column(Integer, default=-1)  # -1 = config default, UTC
    total_users: Mapped[int] = mapped_column(Integer, default=0)
    sent_count: Mapped[int] = mapped_column(Integer, default=0)
    failed_count: Mapped[int] = mapped_column(Integer, default=0)
    status: Mapped[str] = mapped_column(String(20), default="scheduled")  # scheduled | running | completed
    last_batch_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True))


class UpdateEmailSend(IdMixin, TimestampMixin, Base):
    __tablename__ = "update_email_sends"
    __table_args__ = (UniqueConstraint("update_email_id", "user_id"),)

    update_email_id: Mapped[uuid.UUID] = mapped_column(
        Uuid, ForeignKey("update_emails.id", ondelete="CASCADE")
    )
    user_id: Mapped[uuid.UUID] = mapped_column(Uuid, ForeignKey("users.id", ondelete="CASCADE"))
    status: Mapped[str] = mapped_column(String(10))  # sent | failed